]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'sched/urgent'
authorIngo Molnar <mingo@kernel.org>
Sat, 30 Nov 2013 14:23:22 +0000 (15:23 +0100)
committerIngo Molnar <mingo@kernel.org>
Sat, 30 Nov 2013 14:23:22 +0000 (15:23 +0100)
3380 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/Changes
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-omap.txt
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/rng/qcom,prng.txt [new file with mode: 0644]
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/gpio/board.txt [new file with mode: 0644]
Documentation/gpio/consumer.txt [new file with mode: 0644]
Documentation/gpio/driver.txt [new file with mode: 0644]
Documentation/gpio/gpio-legacy.txt [moved from Documentation/gpio.txt with 100% similarity]
Documentation/gpio/gpio.txt [new file with mode: 0644]
Documentation/gpio/sysfs.txt [new file with mode: 0644]
Documentation/hwmon/lm90
Documentation/i2c/busses/i2c-i801
Documentation/input/gamepad.txt
Documentation/kbuild/kconfig.txt
Documentation/kernel-parameters.txt
Documentation/memory-barriers.txt
Documentation/networking/ip-sysctl.txt
Documentation/power/power_supply_class.txt
Documentation/power/runtime_pm.txt
Documentation/pwm.txt
Documentation/robust-futex-ABI.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/bcm2835.dtsi
arch/arm/boot/dts/cros5250-common.dtsi
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/imx6qdl.dtsi
arch/arm/boot/dts/omap-zoom-common.dtsi
arch/arm/boot/dts/omap2.dtsi
arch/arm/boot/dts/omap2420.dtsi
arch/arm/boot/dts/omap2430.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/common.h
arch/arm/mach-omap2/display.c
arch/arm/mach-omap2/gpmc-smsc911x.c
arch/arm/mach-omap2/gpmc.c
arch/arm/mach-omap2/omap-secure.h
arch/arm/mach-omap2/omap4-common.c
arch/arm/mach-omap2/omap_device.c
arch/arm/mach-omap2/pm34xx.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/fuse.c
arch/arm/mach-tegra/tegra.c
arch/arm/mach-vexpress/spc.c
arch/arm/mach-vexpress/spc.h
arch/arm/mach-vexpress/tc2_pm.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/boot/dts/foundation-v8.dts
arch/arm64/include/asm/dma-mapping.h
arch/arm64/include/asm/io.h
arch/arm64/include/asm/irqflags.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/pgtable.h
arch/arm64/include/asm/thread_info.h
arch/arm64/include/asm/xen/page-coherent.h [new file with mode: 0644]
arch/arm64/kernel/debug-monitors.c
arch/arm64/kernel/entry.S
arch/arm64/kernel/ptrace.c
arch/arm64/kernel/setup.c
arch/arm64/kernel/smp.c
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/ar7/setup.c
arch/mips/configs/db1235_defconfig
arch/mips/emma/markeins/setup.c
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/mips/netlogic/xlp/setup.c
arch/mips/netlogic/xlr/setup.c
arch/mips/sibyte/swarm/setup.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/boot/dts/xcalibur1501.dts
arch/powerpc/boot/dts/xpedite5301.dts
arch/powerpc/boot/dts/xpedite5330.dts
arch/powerpc/boot/dts/xpedite5370.dts
arch/powerpc/boot/util.S
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/ppc_asm.h
arch/powerpc/include/asm/processor.h
arch/powerpc/include/asm/pte-book3e.h
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/setup.h
arch/powerpc/include/asm/smp.h
arch/powerpc/include/asm/thread_info.h
arch/powerpc/include/asm/timex.h
arch/powerpc/include/asm/uprobes.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/machine_kexec.c
arch/powerpc/kernel/nvram_64.c
arch/powerpc/kernel/process.c
arch/powerpc/kernel/prom.c
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/setup_64.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/uprobes.c
arch/powerpc/kernel/vdso32/gettimeofday.S
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/hugetlbpage-book3e.c
arch/powerpc/mm/pgtable_32.c
arch/powerpc/mm/pgtable_64.c
arch/powerpc/mm/slice.c
arch/powerpc/mm/tlb_nohash.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/crypto/aes_s390.c
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/page.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/include/asm/vdso.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/compat_signal.c
arch/s390/kernel/crash_dump.c
arch/s390/kernel/early.c
arch/s390/kernel/kprobes.c
arch/s390/kernel/pgm_check.S
arch/s390/kernel/setup.c
arch/s390/kernel/signal.c
arch/s390/kernel/time.c
arch/s390/kernel/vdso32/clock_gettime.S
arch/s390/kernel/vdso32/gettimeofday.S
arch/s390/kernel/vdso64/clock_gettime.S
arch/s390/kernel/vdso64/gettimeofday.S
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/lib/uaccess_pt.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/crypto/Makefile
arch/x86/crypto/aesni-intel_glue.c
arch/x86/crypto/camellia_aesni_avx2_glue.c
arch/x86/crypto/camellia_aesni_avx_glue.c
arch/x86/crypto/cast5_avx_glue.c
arch/x86/crypto/cast6_avx_glue.c
arch/x86/crypto/serpent_avx2_glue.c
arch/x86/crypto/serpent_avx_glue.c
arch/x86/crypto/serpent_sse2_glue.c
arch/x86/crypto/sha256_ssse3_glue.c
arch/x86/crypto/twofish_avx_glue.c
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/simd.h [new file with mode: 0644]
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/trace/irq_vectors.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/Makefile
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/cpu/intel_cacheinfo.c
arch/x86/kernel/cpu/perf_event_intel_rapl.c [new file with mode: 0644]
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/ablk_helper.c [moved from arch/x86/crypto/ablk_helper.c with 95% similarity]
crypto/ablkcipher.c
crypto/af_alg.c
crypto/algif_hash.c
crypto/algif_skcipher.c
crypto/ansi_cprng.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/authenc.c
crypto/authencesn.c
crypto/ccm.c
crypto/gcm.c
crypto/hash_info.c [new file with mode: 0644]
crypto/memneq.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/acresrc.h
drivers/acpi/acpica/nsalloc.c
drivers/acpi/acpica/nsutils.c
drivers/acpi/acpica/rscalc.c
drivers/acpi/acpica/rscreate.c
drivers/acpi/acpica/rsutils.c
drivers/acpi/acpica/utdebug.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/nvs.c
drivers/acpi/pci_root.c
drivers/acpi/scan.c
drivers/acpi/sleep.c
drivers/acpi/sysfs.c
drivers/acpi/video.c
drivers/ata/ahci.c
drivers/ata/ahci_platform.c
drivers/ata/libata-acpi.c
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
drivers/ata/libata-zpodd.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/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/msm-rng.c [new file with mode: 0644]
drivers/char/hw_random/omap3-rom-rng.c [new file with mode: 0644]
drivers/char/hw_random/pseries-rng.c
drivers/char/hw_random/timeriomem-rng.c
drivers/char/hw_random/via-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/exynos4210-cpufreq.c
drivers/cpufreq/exynos4x12-cpufreq.c
drivers/cpufreq/exynos5250-cpufreq.c
drivers/cpufreq/omap-cpufreq.c
drivers/cpufreq/tegra-cpufreq.c
drivers/crypto/caam/Kconfig
drivers/crypto/caam/Makefile
drivers/crypto/caam/caamalg.c
drivers/crypto/caam/caamhash.c
drivers/crypto/caam/caamrng.c
drivers/crypto/caam/ctrl.c
drivers/crypto/caam/desc.h
drivers/crypto/caam/intern.h
drivers/crypto/caam/jr.c
drivers/crypto/caam/jr.h
drivers/crypto/caam/regs.h
drivers/crypto/caam/sg_sw_sec4.h
drivers/crypto/dcp.c
drivers/crypto/ixp4xx_crypto.c
drivers/crypto/mv_cesa.c
drivers/crypto/omap-aes.c
drivers/crypto/omap-sham.c
drivers/crypto/picoxcell_crypto.c
drivers/crypto/sahara.c
drivers/crypto/talitos.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/gpio-bcm-kona.c
drivers/gpio/gpio-msm-v2.c
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-pl061.c
drivers/gpio/gpio-rcar.c
drivers/gpio/gpio-tb10x.c
drivers/gpio/gpio-twl4030.c
drivers/gpio/gpio-ucb1400.c
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_g2d.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_release.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/atombios_i2c.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/cypress_dpm.c
drivers/gpu/drm/radeon/dce6_afmt.c
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreen_dma.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/ni_dpm.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_dma.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_ring.c
drivers/gpu/drm/radeon/radeon_semaphore.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/rv770_dma.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/radeon/trinity_dpm.c
drivers/gpu/drm/radeon/uvd_v1_0.c
drivers/gpu/drm/radeon/uvd_v3_1.c
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_execbuf_util.c
drivers/gpu/drm/ttm/ttm_object.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/Makefile
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_prime.c [new file with mode: 0644]
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
drivers/gpu/drm/vmwgfx/vmwgfx_surface.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-appleir.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-kye.c
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-core.c
drivers/hid/hid-wiimote-modules.c
drivers/hid/hid-wiimote.h
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/uhid.c
drivers/hid/usbhid/hid-quirks.c
drivers/hwmon/Kconfig
drivers/hwmon/acpi_power_meter.c
drivers/hwmon/asus_atk0110.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-diolan-u2c.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/accel/hid-sensor-accel-3d.c
drivers/iio/accel/kxsd9.c
drivers/iio/adc/ad_sigma_delta.c
drivers/iio/adc/at91_adc.c
drivers/iio/adc/mcp3422.c
drivers/iio/adc/nau7802.c
drivers/iio/adc/ti_am335x_adc.c
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
drivers/iio/common/hid-sensors/hid-sensor-trigger.h
drivers/iio/gyro/hid-sensor-gyro-3d.c
drivers/iio/industrialio-event.c
drivers/iio/light/Kconfig
drivers/iio/light/hid-sensor-als.c
drivers/iio/magnetometer/Kconfig
drivers/iio/magnetometer/hid-sensor-magn-3d.c
drivers/iio/magnetometer/mag3110.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/hp_sdc_rtc.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/atmel-wm97xx.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/sur40.c [new file with mode: 0644]
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/irqchip/irq-gic.c
drivers/isdn/isdnloop/isdnloop.c
drivers/isdn/mISDN/socket.c
drivers/lguest/lguest_device.c
drivers/lguest/x86/core.c
drivers/macintosh/Makefile
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_hw.c
drivers/ntb/ntb_hw.h
drivers/ntb/ntb_regs.h
drivers/ntb/ntb_transport.c
drivers/parport/Kconfig
drivers/parport/parport_ip32.c
drivers/pci/ats.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pcie-designware.c
drivers/pci/hotplug/Kconfig
drivers/pci/hotplug/Makefile
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp.h
drivers/pci/hotplug/acpiphp_core.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/acpiphp_ibm.c
drivers/pci/hotplug/cpci_hotplug_core.c
drivers/pci/hotplug/cpci_hotplug_pci.c
drivers/pci/hotplug/cpcihp_generic.c
drivers/pci/hotplug/cpcihp_zt5550.c
drivers/pci/hotplug/cpcihp_zt5550.h
drivers/pci/hotplug/cpqphp_core.c
drivers/pci/hotplug/cpqphp_ctrl.c
drivers/pci/hotplug/cpqphp_pci.c
drivers/pci/hotplug/ibmphp.h
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/ibmphp_hpc.c
drivers/pci/hotplug/ibmphp_pci.c
drivers/pci/hotplug/ibmphp_res.c
drivers/pci/hotplug/pci_hotplug_core.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_acpi.c
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/pcihp_skeleton.c
drivers/pci/hotplug/rpadlpar_core.c
drivers/pci/hotplug/rpaphp.h
drivers/pci/hotplug/rpaphp_core.c
drivers/pci/hotplug/rpaphp_pci.c
drivers/pci/hotplug/rpaphp_slot.c
drivers/pci/hotplug/sgi_hotplug.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_hpc.c
drivers/pci/ioapic.c
drivers/pci/iov.c
drivers/pci/irq.c
drivers/pci/msi.c
drivers/pci/pci-acpi.c
drivers/pci/pci-driver.c
drivers/pci/pci-label.c
drivers/pci/pci-stub.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aspm.c
drivers/pci/pcie/pme.c
drivers/pci/pcie/portdrv.h
drivers/pci/pcie/portdrv_bus.c
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/proc.c
drivers/pci/quirks.c
drivers/pci/remove.c
drivers/pci/search.c
drivers/pci/setup-bus.c
drivers/pci/setup-res.c
drivers/pci/slot.c
drivers/pci/syscall.c
drivers/pinctrl/pinctrl-single.c
drivers/platform/Kconfig
drivers/platform/Makefile
drivers/platform/chrome/Kconfig [new file with mode: 0644]
drivers/platform/chrome/Makefile [new file with mode: 0644]
drivers/platform/chrome/chromeos_laptop.c [moved from drivers/platform/x86/chromeos_laptop.c with 100% similarity]
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/apple-gmux.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_mid_powerbtn.c
drivers/platform/x86/intel_scu_ipc.c
drivers/platform/x86/panasonic-laptop.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/topstar-laptop.c
drivers/platform/x86/toshiba_acpi.c
drivers/platform/x86/wmi.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/arizona-micsupp.c
drivers/regulator/core.c
drivers/regulator/gpio-regulator.c
drivers/regulator/pfuze100-regulator.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/dasd_eckd.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/btmtk_usb/btmtk_usb.c
drivers/staging/comedi/drivers/pcl730.c
drivers/staging/comedi/drivers/s626.c
drivers/staging/comedi/drivers/vmk80xx.c
drivers/staging/ft1000/ft1000-usb/ft1000_download.c
drivers/staging/iio/adc/ad7606.h
drivers/staging/iio/adc/mxs-lradc.c
drivers/staging/iio/magnetometer/Kconfig
drivers/staging/imx-drm/Kconfig
drivers/staging/imx-drm/Makefile
drivers/staging/imx-drm/imx-drm-core.c
drivers/staging/lustre/lustre/ptlrpc/pinger.c
drivers/staging/media/go7007/go7007-usb.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/nvec/nvec.c
drivers/staging/rtl8188eu/core/rtw_ap.c
drivers/staging/tidspbridge/Kconfig
drivers/staging/tidspbridge/core/sync.c
drivers/staging/tidspbridge/include/dspbridge/sync.h
drivers/staging/tidspbridge/rmgr/drv_interface.c
drivers/staging/vt6655/hostap.c
drivers/staging/vt6656/baseband.c
drivers/staging/vt6656/hostap.c
drivers/staging/vt6656/rndis.h
drivers/staging/zram/zram_drv.c
drivers/staging/zsmalloc/zsmalloc-main.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/amiserial.c
drivers/tty/hvc/hvc_xen.c
drivers/tty/metag_da.c
drivers/tty/n_tty.c
drivers/tty/serial/8250/Kconfig
drivers/tty/serial/pmac_zilog.c
drivers/tty/serial/sh-sci.c
drivers/tty/tty_io.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/affs/Changes
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/ceph/addr.c
fs/ceph/cache.c
fs/ceph/caps.c
fs/ceph/dir.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/super.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/cifs/smbfsctl.h
fs/configfs/dir.c
fs/coredump.c
fs/dcache.c
fs/dlm/netlink.c
fs/ecryptfs/crypto.c
fs/ecryptfs/file.c
fs/efivarfs/super.c
fs/exec.c
fs/ext4/balloc.c
fs/ext4/ext4.h
fs/ext4/extents.c
fs/ext4/ialloc.c
fs/ext4/inline.c
fs/ext4/inode.c
fs/ext4/mballoc.c
fs/ext4/mmp.c
fs/ext4/page-io.c
fs/ext4/super.c
fs/ext4/xattr.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/sysfs/file.c
fs/xfs/Makefile
fs/xfs/kmem.c
fs/xfs/kmem.h
fs/xfs/xfs_acl.c
fs/xfs/xfs_ag.h
fs/xfs/xfs_alloc.c
fs/xfs/xfs_alloc.h
fs/xfs/xfs_alloc_btree.c
fs/xfs/xfs_alloc_btree.h
fs/xfs/xfs_aops.c
fs/xfs/xfs_attr.c
fs/xfs/xfs_attr_inactive.c
fs/xfs/xfs_attr_leaf.c
fs/xfs/xfs_attr_leaf.h
fs/xfs/xfs_attr_list.c
fs/xfs/xfs_attr_remote.c
fs/xfs/xfs_attr_remote.h
fs/xfs/xfs_bit.c
fs/xfs/xfs_bmap.c
fs/xfs/xfs_bmap_btree.c
fs/xfs/xfs_bmap_btree.h
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_bmap_util.h
fs/xfs/xfs_btree.c
fs/xfs/xfs_btree.h
fs/xfs/xfs_buf.c
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_buf_item.h
fs/xfs/xfs_da_btree.c
fs/xfs/xfs_da_btree.h
fs/xfs/xfs_da_format.c [new file with mode: 0644]
fs/xfs/xfs_da_format.h [moved from fs/xfs/xfs_dir2_format.h with 60% similarity]
fs/xfs/xfs_dir2.c
fs/xfs/xfs_dir2.h
fs/xfs/xfs_dir2_block.c
fs/xfs/xfs_dir2_data.c
fs/xfs/xfs_dir2_leaf.c
fs/xfs/xfs_dir2_node.c
fs/xfs/xfs_dir2_priv.h
fs/xfs/xfs_dir2_readdir.c
fs/xfs/xfs_dir2_sf.c
fs/xfs/xfs_discard.c
fs/xfs/xfs_dquot.c
fs/xfs/xfs_dquot.h
fs/xfs/xfs_dquot_buf.c [new file with mode: 0644]
fs/xfs/xfs_dquot_item.c
fs/xfs/xfs_error.c
fs/xfs/xfs_export.c
fs/xfs/xfs_extent_busy.c
fs/xfs/xfs_extent_busy.h
fs/xfs/xfs_extfree_item.c
fs/xfs/xfs_file.c
fs/xfs/xfs_filestream.c
fs/xfs/xfs_format.h
fs/xfs/xfs_fs.h
fs/xfs/xfs_fsops.c
fs/xfs/xfs_ialloc.c
fs/xfs/xfs_ialloc.h
fs/xfs/xfs_ialloc_btree.c
fs/xfs/xfs_ialloc_btree.h
fs/xfs/xfs_icache.c
fs/xfs/xfs_icreate_item.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_inode_buf.c
fs/xfs/xfs_inode_buf.h
fs/xfs/xfs_inode_fork.c
fs/xfs/xfs_inode_fork.h
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_ioctl32.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_iomap.h
fs/xfs/xfs_iops.c
fs/xfs/xfs_iops.h
fs/xfs/xfs_itable.c
fs/xfs/xfs_log.c
fs/xfs/xfs_log.h
fs/xfs/xfs_log_cil.c
fs/xfs/xfs_log_format.h
fs/xfs/xfs_log_priv.h
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_log_rlimit.c
fs/xfs/xfs_message.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_qm.c
fs/xfs/xfs_qm.h
fs/xfs/xfs_qm_bhv.c
fs/xfs/xfs_qm_syscalls.c
fs/xfs/xfs_quota.h
fs/xfs/xfs_quota_defs.h
fs/xfs/xfs_quotaops.c
fs/xfs/xfs_rtalloc.c
fs/xfs/xfs_rtalloc.h
fs/xfs/xfs_rtbitmap.c [new file with mode: 0644]
fs/xfs/xfs_sb.c
fs/xfs/xfs_sb.h
fs/xfs/xfs_shared.h [new file with mode: 0644]
fs/xfs/xfs_super.c
fs/xfs/xfs_symlink.c
fs/xfs/xfs_symlink.h
fs/xfs/xfs_symlink_remote.c
fs/xfs/xfs_trace.c
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans.c
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_ail.c
fs/xfs/xfs_trans_buf.c
fs/xfs/xfs_trans_dquot.c
fs/xfs/xfs_trans_extfree.c
fs/xfs/xfs_trans_inode.c
fs/xfs/xfs_trans_priv.h
fs/xfs/xfs_trans_resv.c
fs/xfs/xfs_vnode.h
fs/xfs/xfs_xattr.c
include/acpi/acconfig.h
include/acpi/acpi_bus.h
include/acpi/acpixf.h
include/asm-generic/memory_model.h
include/asm-generic/simd.h [new file with mode: 0644]
include/asm-generic/vmlinux.lds.h
include/crypto/ablk_helper.h [moved from arch/x86/include/asm/crypto/ablk_helper.h with 100% similarity]
include/crypto/algapi.h
include/crypto/authenc.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_bo_api.h
include/drm/ttm/ttm_execbuf_util.h
include/drm/ttm/ttm_object.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/gpio/driver.h
include/linux/hid-sensor-hub.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/irqreturn.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/msi.h
include/linux/net.h
include/linux/netdevice.h
include/linux/nfs4.h
include/linux/padata.h
include/linux/pci-acpi.h
include/linux/pci.h
include/linux/pci_hotplug.h
include/linux/pcieport_if.h
include/linux/perf_event.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/tegra-powergate.h
include/linux/tracepoint.h
include/linux/uprobes.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/radeon_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/pci_regs.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/cpuset.c
kernel/events/core.c
kernel/events/uprobes.c
kernel/extable.c
kernel/fork.c
kernel/hung_task.c
kernel/irq/chip.c
kernel/irq/manage.c
kernel/irq/pm.c
kernel/irq/settings.h
kernel/irq/spurious.c
kernel/kexec.c
kernel/locking/lockdep.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/padata.c
kernel/panic.c
kernel/power/snapshot.c
kernel/power/user.c
kernel/rcu/tiny.c
kernel/rcu/tree.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/sched.h
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_event_perf.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
kernel/workqueue.c
lib/Kconfig
lib/Kconfig.debug
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/amdtp.c
sound/firewire/amdtp.h
sound/firewire/dice.c
sound/isa/msnd/msnd_pinnacle.c
sound/isa/wavefront/wavefront_synth.c
sound/pci/hda/Kconfig
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.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/ab8500-codec.c
sound/soc/codecs/alc5632.c
sound/soc/codecs/arizona.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/soc/sh/rcar/core.c
sound/soc/sh/rcar/scu.c
sound/sparc/cs4231.c
sound/usb/endpoint.c
sound/usb/mixer_quirks.c
sound/usb/stream.c
tools/lib/lockdep/Makefile [new file with mode: 0644]
tools/lib/lockdep/common.c [new file with mode: 0644]
tools/lib/lockdep/include/liblockdep/common.h [new file with mode: 0644]
tools/lib/lockdep/include/liblockdep/mutex.h [new file with mode: 0644]
tools/lib/lockdep/include/liblockdep/rwlock.h [new file with mode: 0644]
tools/lib/lockdep/lockdep [new file with mode: 0755]
tools/lib/lockdep/lockdep.c [new file with mode: 0644]
tools/lib/lockdep/lockdep_internals.h [new file with mode: 0644]
tools/lib/lockdep/lockdep_states.h [new file with mode: 0644]
tools/lib/lockdep/preload.c [new file with mode: 0644]
tools/lib/lockdep/rbtree.c [new file with mode: 0644]
tools/lib/lockdep/run_tests.sh [new file with mode: 0644]
tools/lib/lockdep/tests/AA.c [new file with mode: 0644]
tools/lib/lockdep/tests/ABBA.c [new file with mode: 0644]
tools/lib/lockdep/tests/ABBCCA.c [new file with mode: 0644]
tools/lib/lockdep/tests/ABBCCDDA.c [new file with mode: 0644]
tools/lib/lockdep/tests/ABCABC.c [new file with mode: 0644]
tools/lib/lockdep/tests/ABCDBCDA.c [new file with mode: 0644]
tools/lib/lockdep/tests/ABCDBDDA.c [new file with mode: 0644]
tools/lib/lockdep/tests/WW.c [new file with mode: 0644]
tools/lib/lockdep/tests/common.h [new file with mode: 0644]
tools/lib/lockdep/tests/unlock_balance.c [new file with mode: 0644]
tools/lib/lockdep/uinclude/asm/hweight.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/asm/sections.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/bitops.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/compiler.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/debug_locks.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/delay.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/export.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/ftrace.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/gfp.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/hardirq.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/hash.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/interrupt.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/irqflags.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/kallsyms.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/kern_levels.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/kernel.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/kmemcheck.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/linkage.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/list.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/lockdep.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/module.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/mutex.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/poison.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/prefetch.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/proc_fs.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/rbtree.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/rbtree_augmented.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/rcu.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/seq_file.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/spinlock.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/stacktrace.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/stringify.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/linux/types.h [new file with mode: 0644]
tools/lib/lockdep/uinclude/trace/events/lock.h [new file with mode: 0644]
tools/lib/traceevent/event-parse.c
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-script.txt
tools/perf/Documentation/perf-timechart.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Makefile
tools/perf/Makefile.perf
tools/perf/builtin-probe.c
tools/perf/builtin-record.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-timechart.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/perf-completion.sh [moved from tools/perf/bash_completion with 65% similarity]
tools/perf/perf.h
tools/perf/tests/attr/test-record-no-inherit
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/header.c
tools/perf/util/machine.c
tools/perf/util/parse-events.c
tools/perf/util/parse-options.c
tools/perf/util/parse-options.h
tools/perf/util/pmu.c
tools/perf/util/pmu.h
tools/perf/util/session.c
tools/perf/util/svghelper.c
tools/perf/util/svghelper.h
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/perf/util/target.c
tools/perf/util/target.h
tools/perf/util/thread.c
tools/perf/util/thread.h
tools/perf/util/unwind.c
tools/perf/util/unwind.h
tools/power/cpupower/man/cpupower-idle-info.1
tools/power/cpupower/man/cpupower-idle-set.1 [new file with mode: 0644]
tools/power/cpupower/utils/helpers/sysfs.c
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 5c53d28f775cde36f4e77f46e41dc9ead66aa9c1..b9688de8455bb1577ef923af907bcbf86da0f0e4 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 b36fb0dc13c8ee14a8ca8c58bd2792ee1376f657..ec7af69fea0afd9fe57f6600adc6b9be8fceb90d 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 ed5dd567d397bab239a0ffe5abbe7696e24fce61..39dfa5cb1cc56c41af0ca297de0d3c003655ce2d 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 b1758088527339466c5d5eb8be6151eabe02952d..07c75d18154e7608f9368caaf6fbbca2baa6aafd 100644 (file)
@@ -196,13 +196,6 @@ chmod 0644 /dev/cpu/microcode
 as root before you can use this.  You'll probably also want to
 get the user-space microcode_ctl utility to use with this.
 
-Powertweak
-----------
-
-If you are running v0.1.17 or earlier, you should upgrade to
-version v0.99.0 or higher. Running old versions may cause problems
-with programs using shared memory.
-
 udev
 ----
 udev is a userspace application for populating /dev dynamically with
@@ -366,10 +359,6 @@ Intel P6 microcode
 ------------------
 o  <http://www.urbanmyth.org/microcode/>
 
-Powertweak
-----------
-o  <http://powertweak.sourceforge.net/>
-
 udev
 ----
 o <http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html>
index e59480db9ee0acb6be2793a9e48ea4e55d1bf5e3..cc2450d80310a5b8b0d979a23bd469c2d22ec130 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 f770ac0893d4acbfe0e412e0508317016d60a0e7..049675944b78035b5829ed94d53f5c0d2d45fc5b 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 f839acd6f0ee3af5d6c626ae0b12029580156bda..ad911556961144cb76958411c4a95a7d5dfde279 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 e1f343c7a34b7b10ea462a39b5e9a320ef463ba6..f69bcf5a6343bf314b5eef199999c5a676131e74 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>;
+       };
+};
index 56564aa4b444addcf9b770bfd4dbe6476f2b3d4d..7e49839d41249ca5168b0de1ea02781a2798486d 100644 (file)
@@ -1,7 +1,8 @@
 I2C for OMAP platforms
 
 Required properties :
-- compatible : Must be "ti,omap3-i2c" or "ti,omap4-i2c"
+- compatible : Must be "ti,omap2420-i2c", "ti,omap2430-i2c", "ti,omap3-i2c"
+  or "ti,omap4-i2c"
 - ti,hwmods : Must be "i2c<n>", n being the instance number (1-based)
 - #address-cells = <1>;
 - #size-cells = <0>;
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 ad6a73852f0880bf3625893d6ffc11646bdc8048..b1cb3415e6f1be9139a5af5622e8e38114358a54 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 491c97b783843c733444c60c00e0c7cf41843138..878549ba814d5d1684ff2b5a960bc1725854e3f2 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 c9332c6260214bf8d4e065b05257dbb10f669c53..78a840d7510d27ab51eb892542318e01a6bc3774 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 1dd622546d06b711bf262358b387b4bb9af3f68a..9046ba06c47ab63570f99769455dcfeeea020e10 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 066a78b034ca8589e4de4d31183075768496f81a..8f3f13315358028f20a5a79720e6a27266caf2be 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 2a4b4bce6110af59579c6f29e36fb8bcd71f7428..7fc1b010fa759a9cb69ab286b4ca032d295f0046 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 d61fccd40bad42a746374c29f8589891bb75621f..5538de9c20077566c4dec8243fcba0f78778f4fc 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)
diff --git a/Documentation/devicetree/bindings/rng/qcom,prng.txt b/Documentation/devicetree/bindings/rng/qcom,prng.txt
new file mode 100644 (file)
index 0000000..8e5853c
--- /dev/null
@@ -0,0 +1,17 @@
+Qualcomm MSM pseudo random number generator.
+
+Required properties:
+
+- compatible  : should be "qcom,prng"
+- reg         : specifies base physical address and size of the registers map
+- clocks      : phandle to clock-controller plus clock-specifier pair
+- clock-names : "core" clocks all registers, FIFO and circuits in PRNG IP block
+
+Example:
+
+       rng@f9bff000 {
+               compatible = "qcom,prng";
+               reg = <0xf9bff000 0x200>;
+               clocks = <&clock GCC_PRNG_AHB_CLK>;
+               clock-names = "core";
+       };
index 4c85c4c69584a14d39dd8597facb920b4b69cf46..2ba5f9c023acb93be4e8089fd3c817a44cd08bc7 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 1e4fc727f3b180f84242a3d77b3854ad113b4a60..764db86d441ad131aceca0e15146d5c914d97919 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 a2b5663eae266d2dcae8fcf9400ef9eb16b24709..dd77a81bdb80b82b5c732ceb33ba2ef30b1d9eea 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 9dae59407437916759a73e00166b6ba04b2c4e52..5dd282dda55c5eca0fe50b6b1cfc0116d3cc7432 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
diff --git a/Documentation/gpio/board.txt b/Documentation/gpio/board.txt
new file mode 100644 (file)
index 0000000..0d03506
--- /dev/null
@@ -0,0 +1,115 @@
+GPIO Mappings
+=============
+
+This document explains how GPIOs can be assigned to given devices and functions.
+Note that it only applies to the new descriptor-based interface. For a
+description of the deprecated integer-based GPIO interface please refer to
+gpio-legacy.txt (actually, there is no real mapping possible with the old
+interface; you just fetch an integer from somewhere and request the
+corresponding GPIO.
+
+Platforms that make use of GPIOs must select ARCH_REQUIRE_GPIOLIB (if GPIO usage
+is mandatory) or ARCH_WANT_OPTIONAL_GPIOLIB (if GPIO support can be omitted) in
+their Kconfig. Then, how GPIOs are mapped depends on what the platform uses to
+describe its hardware layout. Currently, mappings can be defined through device
+tree, ACPI, and platform data.
+
+Device Tree
+-----------
+GPIOs can easily be mapped to devices and functions in the device tree. The
+exact way to do it depends on the GPIO controller providing the GPIOs, see the
+device tree bindings for your controller.
+
+GPIOs mappings are defined in the consumer device's node, in a property named
+<function>-gpios, where <function> is the function the driver will request
+through gpiod_get(). For example:
+
+       foo_device {
+               compatible = "acme,foo";
+               ...
+               led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
+                           <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
+                           <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
+
+               power-gpio = <&gpio 1 GPIO_ACTIVE_LOW>;
+       };
+
+This property will make GPIOs 15, 16 and 17 available to the driver under the
+"led" function, and GPIO 1 as the "power" GPIO:
+
+       struct gpio_desc *red, *green, *blue, *power;
+
+       red = gpiod_get_index(dev, "led", 0);
+       green = gpiod_get_index(dev, "led", 1);
+       blue = gpiod_get_index(dev, "led", 2);
+
+       power = gpiod_get(dev, "power");
+
+The led GPIOs will be active-high, while the power GPIO will be active-low (i.e.
+gpiod_is_active_low(power) will be true).
+
+ACPI
+----
+ACPI does not support function names for GPIOs. Therefore, only the "idx"
+argument of gpiod_get_index() is useful to discriminate between GPIOs assigned
+to a device. The "con_id" argument can still be set for debugging purposes (it
+will appear under error messages as well as debug and sysfs nodes).
+
+Platform Data
+-------------
+Finally, GPIOs can be bound to devices and functions using platform data. Board
+files that desire to do so need to include the following header:
+
+       #include <linux/gpio/driver.h>
+
+GPIOs are mapped by the means of tables of lookups, containing instances of the
+gpiod_lookup structure. Two macros are defined to help declaring such mappings:
+
+       GPIO_LOOKUP(chip_label, chip_hwnum, dev_id, con_id, flags)
+       GPIO_LOOKUP_IDX(chip_label, chip_hwnum, dev_id, con_id, idx, flags)
+
+where
+
+  - chip_label is the label of the gpiod_chip instance providing the GPIO
+  - chip_hwnum is the hardware number of the GPIO within the chip
+  - dev_id is the identifier of the device that will make use of this GPIO. If
+       NULL, the GPIO will be available to all devices.
+  - con_id is the name of the GPIO function from the device point of view. It
+       can be NULL.
+  - idx is the index of the GPIO within the function.
+  - flags is defined to specify the following properties:
+       * GPIOF_ACTIVE_LOW      - to configure the GPIO as active-low
+       * GPIOF_OPEN_DRAIN      - GPIO pin is open drain type.
+       * GPIOF_OPEN_SOURCE     - GPIO pin is open source type.
+
+In the future, these flags might be extended to support more properties.
+
+Note that GPIO_LOOKUP() is just a shortcut to GPIO_LOOKUP_IDX() where idx = 0.
+
+A lookup table can then be defined as follows:
+
+       struct gpiod_lookup gpios_table[] = {
+       GPIO_LOOKUP_IDX("gpio.0", 15, "foo.0", "led", 0, GPIO_ACTIVE_HIGH),
+       GPIO_LOOKUP_IDX("gpio.0", 16, "foo.0", "led", 1, GPIO_ACTIVE_HIGH),
+       GPIO_LOOKUP_IDX("gpio.0", 17, "foo.0", "led", 2, GPIO_ACTIVE_HIGH),
+       GPIO_LOOKUP("gpio.0", 1, "foo.0", "power", GPIO_ACTIVE_LOW),
+       };
+
+And the table can be added by the board code as follows:
+
+       gpiod_add_table(gpios_table, ARRAY_SIZE(gpios_table));
+
+The driver controlling "foo.0" will then be able to obtain its GPIOs as follows:
+
+       struct gpio_desc *red, *green, *blue, *power;
+
+       red = gpiod_get_index(dev, "led", 0);
+       green = gpiod_get_index(dev, "led", 1);
+       blue = gpiod_get_index(dev, "led", 2);
+
+       power = gpiod_get(dev, "power");
+       gpiod_direction_output(power, 1);
+
+Since the "power" GPIO is mapped as active-low, its actual signal will be 0
+after this code. Contrary to the legacy integer GPIO interface, the active-low
+property is handled during mapping and is thus transparent to GPIO consumers.
diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt
new file mode 100644 (file)
index 0000000..07c74a3
--- /dev/null
@@ -0,0 +1,197 @@
+GPIO Descriptor Consumer Interface
+==================================
+
+This document describes the consumer interface of the GPIO framework. Note that
+it describes the new descriptor-based interface. For a description of the
+deprecated integer-based GPIO interface please refer to gpio-legacy.txt.
+
+
+Guidelines for GPIOs consumers
+==============================
+
+Drivers that can't work without standard GPIO calls should have Kconfig entries
+that depend on GPIOLIB. The functions that allow a driver to obtain and use
+GPIOs are available by including the following file:
+
+       #include <linux/gpio/consumer.h>
+
+All the functions that work with the descriptor-based GPIO interface are
+prefixed with gpiod_. The gpio_ prefix is used for the legacy interface. No
+other function in the kernel should use these prefixes.
+
+
+Obtaining and Disposing GPIOs
+=============================
+
+With the descriptor-based interface, GPIOs are identified with an opaque,
+non-forgeable handler that must be obtained through a call to one of the
+gpiod_get() functions. Like many other kernel subsystems, gpiod_get() takes the
+device that will use the GPIO and the function the requested GPIO is supposed to
+fulfill:
+
+       struct gpio_desc *gpiod_get(struct device *dev, const char *con_id)
+
+If a function is implemented by using several GPIOs together (e.g. a simple LED
+device that displays digits), an additional index argument can be specified:
+
+       struct gpio_desc *gpiod_get_index(struct device *dev,
+                                         const char *con_id, unsigned int idx)
+
+Both functions return either a valid GPIO descriptor, or an error code checkable
+with IS_ERR(). They will never return a NULL pointer.
+
+Device-managed variants of these functions are also defined:
+
+       struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id)
+
+       struct gpio_desc *devm_gpiod_get_index(struct device *dev,
+                                              const char *con_id,
+                                              unsigned int idx)
+
+A GPIO descriptor can be disposed of using the gpiod_put() function:
+
+       void gpiod_put(struct gpio_desc *desc)
+
+It is strictly forbidden to use a descriptor after calling this function. The
+device-managed variant is, unsurprisingly:
+
+       void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
+
+
+Using GPIOs
+===========
+
+Setting Direction
+-----------------
+The first thing a driver must do with a GPIO is setting its direction. This is
+done by invoking one of the gpiod_direction_*() functions:
+
+       int gpiod_direction_input(struct gpio_desc *desc)
+       int gpiod_direction_output(struct gpio_desc *desc, int value)
+
+The return value is zero for success, else a negative errno. It should be
+checked, since the get/set calls don't return errors and since misconfiguration
+is possible. You should normally issue these calls from a task context. However,
+for spinlock-safe GPIOs it is OK to use them before tasking is enabled, as part
+of early board setup.
+
+For output GPIOs, the value provided becomes the initial output value. This
+helps avoid signal glitching during system startup.
+
+A driver can also query the current direction of a GPIO:
+
+       int gpiod_get_direction(const struct gpio_desc *desc)
+
+This function will return either GPIOF_DIR_IN or GPIOF_DIR_OUT.
+
+Be aware that there is no default direction for GPIOs. Therefore, **using a GPIO
+without setting its direction first is illegal and will result in undefined
+behavior!**
+
+
+Spinlock-Safe GPIO Access
+-------------------------
+Most GPIO controllers can be accessed with memory read/write instructions. Those
+don't need to sleep, and can safely be done from inside hard (non-threaded) IRQ
+handlers and similar contexts.
+
+Use the following calls to access GPIOs from an atomic context:
+
+       int gpiod_get_value(const struct gpio_desc *desc);
+       void gpiod_set_value(struct gpio_desc *desc, int value);
+
+The values are boolean, zero for low, nonzero for high. When reading the value
+of an output pin, the value returned should be what's seen on the pin. That
+won't always match the specified output value, because of issues including
+open-drain signaling and output latencies.
+
+The get/set calls do not return errors because "invalid GPIO" should have been
+reported earlier from gpiod_direction_*(). However, note that not all platforms
+can read the value of output pins; those that can't should always return zero.
+Also, using these calls for GPIOs that can't safely be accessed without sleeping
+(see below) is an error.
+
+
+GPIO Access That May Sleep
+--------------------------
+Some GPIO controllers must be accessed using message based buses like I2C or
+SPI. Commands to read or write those GPIO values require waiting to get to the
+head of a queue to transmit a command and get its response. This requires
+sleeping, which can't be done from inside IRQ handlers.
+
+Platforms that support this type of GPIO distinguish them from other GPIOs by
+returning nonzero from this call:
+
+       int gpiod_cansleep(const struct gpio_desc *desc)
+
+To access such GPIOs, a different set of accessors is defined:
+
+       int gpiod_get_value_cansleep(const struct gpio_desc *desc)
+       void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
+
+Accessing such GPIOs requires a context which may sleep, for example a threaded
+IRQ handler, and those accessors must be used instead of spinlock-safe
+accessors without the cansleep() name suffix.
+
+Other than the fact that these accessors might sleep, and will work on GPIOs
+that can't be accessed from hardIRQ handlers, these calls act the same as the
+spinlock-safe calls.
+
+
+Active-low State and Raw GPIO Values
+------------------------------------
+Device drivers like to manage the logical state of a GPIO, i.e. the value their
+device will actually receive, no matter what lies between it and the GPIO line.
+In some cases, it might make sense to control the actual GPIO line value. The
+following set of calls ignore the active-low property of a GPIO and work on the
+raw line value:
+
+       int gpiod_get_raw_value(const struct gpio_desc *desc)
+       void gpiod_set_raw_value(struct gpio_desc *desc, int value)
+       int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
+       void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
+
+The active-low state of a GPIO can also be queried using the following call:
+
+       int gpiod_is_active_low(const struct gpio_desc *desc)
+
+Note that these functions should only be used with great moderation ; a driver
+should not have to care about the physical line level.
+
+GPIOs mapped to IRQs
+--------------------
+GPIO lines can quite often be used as IRQs. You can get the IRQ number
+corresponding to a given GPIO using the following call:
+
+       int gpiod_to_irq(const struct gpio_desc *desc)
+
+It will return an IRQ number, or an negative errno code if the mapping can't be
+done (most likely because that particular GPIO cannot be used as IRQ). It is an
+unchecked error to use a GPIO that wasn't set up as an input using
+gpiod_direction_input(), or to use an IRQ number that didn't originally come
+from gpiod_to_irq(). gpiod_to_irq() is not allowed to sleep.
+
+Non-error values returned from gpiod_to_irq() can be passed to request_irq() or
+free_irq(). They will often be stored into IRQ resources for platform devices,
+by the board-specific initialization code. Note that IRQ trigger options are
+part of the IRQ interface, e.g. IRQF_TRIGGER_FALLING, as are system wakeup
+capabilities.
+
+
+Interacting With the Legacy GPIO Subsystem
+==========================================
+Many kernel subsystems still handle GPIOs using the legacy integer-based
+interface. Although it is strongly encouraged to upgrade them to the safer
+descriptor-based API, the following two functions allow you to convert a GPIO
+descriptor into the GPIO integer namespace and vice-versa:
+
+       int desc_to_gpio(const struct gpio_desc *desc)
+       struct gpio_desc *gpio_to_desc(unsigned gpio)
+
+The GPIO number returned by desc_to_gpio() can be safely used as long as the
+GPIO descriptor has not been freed. All the same, a GPIO number passed to
+gpio_to_desc() must have been properly acquired, and usage of the returned GPIO
+descriptor is only possible after the GPIO number has been released.
+
+Freeing a GPIO obtained by one API with the other API is forbidden and an
+unchecked error.
diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt
new file mode 100644 (file)
index 0000000..9da0bfa
--- /dev/null
@@ -0,0 +1,75 @@
+GPIO Descriptor Driver Interface
+================================
+
+This document serves as a guide for GPIO chip drivers writers. Note that it
+describes the new descriptor-based interface. For a description of the
+deprecated integer-based GPIO interface please refer to gpio-legacy.txt.
+
+Each GPIO controller driver needs to include the following header, which defines
+the structures used to define a GPIO driver:
+
+       #include <linux/gpio/driver.h>
+
+
+Internal Representation of GPIOs
+================================
+
+Inside a GPIO driver, individual GPIOs are identified by their hardware number,
+which is a unique number between 0 and n, n being the number of GPIOs managed by
+the chip. This number is purely internal: the hardware number of a particular
+GPIO descriptor is never made visible outside of the driver.
+
+On top of this internal number, each GPIO also need to have a global number in
+the integer GPIO namespace so that it can be used with the legacy GPIO
+interface. Each chip must thus have a "base" number (which can be automatically
+assigned), and for each GPIO the global number will be (base + hardware number).
+Although the integer representation is considered deprecated, it still has many
+users and thus needs to be maintained.
+
+So for example one platform could use numbers 32-159 for GPIOs, with a
+controller defining 128 GPIOs at a "base" of 32 ; while another platform uses
+numbers 0..63 with one set of GPIO controllers, 64-79 with another type of GPIO
+controller, and on one particular board 80-95 with an FPGA. The numbers need not
+be contiguous; either of those platforms could also use numbers 2000-2063 to
+identify GPIOs in a bank of I2C GPIO expanders.
+
+
+Controller Drivers: gpio_chip
+=============================
+
+In the gpiolib framework each GPIO controller is packaged as a "struct
+gpio_chip" (see linux/gpio/driver.h for its complete definition) with members
+common to each controller of that type:
+
+ - methods to establish GPIO direction
+ - methods used to access GPIO values
+ - method to return the IRQ number associated to a given GPIO
+ - flag saying whether calls to its methods may sleep
+ - optional debugfs dump method (showing extra state like pullup config)
+ - optional base number (will be automatically assigned if omitted)
+ - label for diagnostics and GPIOs mapping using platform data
+
+The code implementing a gpio_chip should support multiple instances of the
+controller, possibly using the driver model. That code will configure each
+gpio_chip and issue gpiochip_add(). Removing a GPIO controller should be rare;
+use gpiochip_remove() when it is unavoidable.
+
+Most often a gpio_chip is part of an instance-specific structure with state not
+exposed by the GPIO interfaces, such as addressing, power management, and more.
+Chips such as codecs will have complex non-GPIO state.
+
+Any debugfs dump method should normally ignore signals which haven't been
+requested as GPIOs. They can use gpiochip_is_requested(), which returns either
+NULL or the label associated with that GPIO when it was requested.
+
+Locking IRQ usage
+-----------------
+Input GPIOs can be used as IRQ signals. When this happens, a driver is requested
+to mark the GPIO as being used as an IRQ:
+
+       int gpiod_lock_as_irq(struct gpio_desc *desc)
+
+This will prevent the use of non-irq related GPIO APIs until the GPIO IRQ lock
+is released:
+
+       void gpiod_unlock_as_irq(struct gpio_desc *desc)
diff --git a/Documentation/gpio/gpio.txt b/Documentation/gpio/gpio.txt
new file mode 100644 (file)
index 0000000..cd9b356
--- /dev/null
@@ -0,0 +1,119 @@
+GPIO Interfaces
+===============
+
+The documents in this directory give detailed instructions on how to access
+GPIOs in drivers, and how to write a driver for a device that provides GPIOs
+itself.
+
+Due to the history of GPIO interfaces in the kernel, there are two different
+ways to obtain and use GPIOs:
+
+  - The descriptor-based interface is the preferred way to manipulate GPIOs,
+and is described by all the files in this directory excepted gpio-legacy.txt.
+  - The legacy integer-based interface which is considered deprecated (but still
+usable for compatibility reasons) is documented in gpio-legacy.txt.
+
+The remainder of this document applies to the new descriptor-based interface.
+gpio-legacy.txt contains the same information applied to the legacy
+integer-based interface.
+
+
+What is a GPIO?
+===============
+
+A "General Purpose Input/Output" (GPIO) is a flexible software-controlled
+digital signal. They are provided from many kinds of chip, and are familiar
+to Linux developers working with embedded and custom hardware. Each GPIO
+represents a bit connected to a particular pin, or "ball" on Ball Grid Array
+(BGA) packages. Board schematics show which external hardware connects to
+which GPIOs. Drivers can be written generically, so that board setup code
+passes such pin configuration data to drivers.
+
+System-on-Chip (SOC) processors heavily rely on GPIOs. In some cases, every
+non-dedicated pin can be configured as a GPIO; and most chips have at least
+several dozen of them. Programmable logic devices (like FPGAs) can easily
+provide GPIOs; multifunction chips like power managers, and audio codecs
+often have a few such pins to help with pin scarcity on SOCs; and there are
+also "GPIO Expander" chips that connect using the I2C or SPI serial buses.
+Most PC southbridges have a few dozen GPIO-capable pins (with only the BIOS
+firmware knowing how they're used).
+
+The exact capabilities of GPIOs vary between systems. Common options:
+
+  - Output values are writable (high=1, low=0). Some chips also have
+    options about how that value is driven, so that for example only one
+    value might be driven, supporting "wire-OR" and similar schemes for the
+    other value (notably, "open drain" signaling).
+
+  - Input values are likewise readable (1, 0). Some chips support readback
+    of pins configured as "output", which is very useful in such "wire-OR"
+    cases (to support bidirectional signaling). GPIO controllers may have
+    input de-glitch/debounce logic, sometimes with software controls.
+
+  - Inputs can often be used as IRQ signals, often edge triggered but
+    sometimes level triggered. Such IRQs may be configurable as system
+    wakeup events, to wake the system from a low power state.
+
+  - Usually a GPIO will be configurable as either input or output, as needed
+    by different product boards; single direction ones exist too.
+
+  - Most GPIOs can be accessed while holding spinlocks, but those accessed
+    through a serial bus normally can't. Some systems support both types.
+
+On a given board each GPIO is used for one specific purpose like monitoring
+MMC/SD card insertion/removal, detecting card write-protect status, driving
+a LED, configuring a transceiver, bit-banging a serial bus, poking a hardware
+watchdog, sensing a switch, and so on.
+
+
+Common GPIO Properties
+======================
+
+These properties are met through all the other documents of the GPIO interface
+and it is useful to understand them, especially if you need to define GPIO
+mappings.
+
+Active-High and Active-Low
+--------------------------
+It is natural to assume that a GPIO is "active" when its output signal is 1
+("high"), and inactive when it is 0 ("low"). However in practice the signal of a
+GPIO may be inverted before is reaches its destination, or a device could decide
+to have different conventions about what "active" means. Such decisions should
+be transparent to device drivers, therefore it is possible to define a GPIO as
+being either active-high ("1" means "active", the default) or active-low ("0"
+means "active") so that drivers only need to worry about the logical signal and
+not about what happens at the line level.
+
+Open Drain and Open Source
+--------------------------
+Sometimes shared signals need to use "open drain" (where only the low signal
+level is actually driven), or "open source" (where only the high signal level is
+driven) signaling. That term applies to CMOS transistors; "open collector" is
+used for TTL. A pullup or pulldown resistor causes the high or low signal level.
+This is sometimes called a "wire-AND"; or more practically, from the negative
+logic (low=true) perspective this is a "wire-OR".
+
+One common example of an open drain signal is a shared active-low IRQ line.
+Also, bidirectional data bus signals sometimes use open drain signals.
+
+Some GPIO controllers directly support open drain and open source outputs; many
+don't. When you need open drain signaling but your hardware doesn't directly
+support it, there's a common idiom you can use to emulate it with any GPIO pin
+that can be used as either an input or an output:
+
+ LOW:  gpiod_direction_output(gpio, 0) ... this drives the signal and overrides
+       the pullup.
+
+ HIGH: gpiod_direction_input(gpio) ... this turns off the output, so the pullup
+       (or some other device) controls the signal.
+
+The same logic can be applied to emulate open source signaling, by driving the
+high signal and configuring the GPIO as input for low. This open drain/open
+source emulation can be handled transparently by the GPIO framework.
+
+If you are "driving" the signal high but gpiod_get_value(gpio) reports a low
+value (after the appropriate rise time passes), you know some other component is
+driving the shared signal low. That's not necessarily an error. As one common
+example, that's how I2C clocks are stretched:  a slave that needs a slower clock
+delays the rising edge of SCK, and the I2C master adjusts its signaling rate
+accordingly.
diff --git a/Documentation/gpio/sysfs.txt b/Documentation/gpio/sysfs.txt
new file mode 100644 (file)
index 0000000..c2c3a97
--- /dev/null
@@ -0,0 +1,155 @@
+GPIO Sysfs Interface for Userspace
+==================================
+
+Platforms which use the "gpiolib" implementors framework may choose to
+configure a sysfs user interface to GPIOs. This is different from the
+debugfs interface, since it provides control over GPIO direction and
+value instead of just showing a gpio state summary. Plus, it could be
+present on production systems without debugging support.
+
+Given appropriate hardware documentation for the system, userspace could
+know for example that GPIO #23 controls the write protect line used to
+protect boot loader segments in flash memory. System upgrade procedures
+may need to temporarily remove that protection, first importing a GPIO,
+then changing its output state, then updating the code before re-enabling
+the write protection. In normal use, GPIO #23 would never be touched,
+and the kernel would have no need to know about it.
+
+Again depending on appropriate hardware documentation, on some systems
+userspace GPIO can be used to determine system configuration data that
+standard kernels won't know about. And for some tasks, simple userspace
+GPIO drivers could be all that the system really needs.
+
+Note that standard kernel drivers exist for common "LEDs and Buttons"
+GPIO tasks:  "leds-gpio" and "gpio_keys", respectively. Use those
+instead of talking directly to the GPIOs; they integrate with kernel
+frameworks better than your userspace code could.
+
+
+Paths in Sysfs
+--------------
+There are three kinds of entry in /sys/class/gpio:
+
+   -   Control interfaces used to get userspace control over GPIOs;
+
+   -   GPIOs themselves; and
+
+   -   GPIO controllers ("gpio_chip" instances).
+
+That's in addition to standard files including the "device" symlink.
+
+The control interfaces are write-only:
+
+    /sys/class/gpio/
+
+       "export" ... Userspace may ask the kernel to export control of
+               a GPIO to userspace by writing its number to this file.
+
+               Example:  "echo 19 > export" will create a "gpio19" node
+               for GPIO #19, if that's not requested by kernel code.
+
+       "unexport" ... Reverses the effect of exporting to userspace.
+
+               Example:  "echo 19 > unexport" will remove a "gpio19"
+               node exported using the "export" file.
+
+GPIO signals have paths like /sys/class/gpio/gpio42/ (for GPIO #42)
+and have the following read/write attributes:
+
+    /sys/class/gpio/gpioN/
+
+       "direction" ... reads as either "in" or "out". This value may
+               normally be written. Writing as "out" defaults to
+               initializing the value as low. To ensure glitch free
+               operation, values "low" and "high" may be written to
+               configure the GPIO as an output with that initial value.
+
+               Note that this attribute *will not exist* if the kernel
+               doesn't support changing the direction of a GPIO, or
+               it was exported by kernel code that didn't explicitly
+               allow userspace to reconfigure this GPIO's direction.
+
+       "value" ... reads as either 0 (low) or 1 (high). If the GPIO
+               is configured as an output, this value may be written;
+               any nonzero value is treated as high.
+
+               If the pin can be configured as interrupt-generating interrupt
+               and if it has been configured to generate interrupts (see the
+               description of "edge"), you can poll(2) on that file and
+               poll(2) will return whenever the interrupt was triggered. If
+               you use poll(2), set the events POLLPRI and POLLERR. If you
+               use select(2), set the file descriptor in exceptfds. After
+               poll(2) returns, either lseek(2) to the beginning of the sysfs
+               file and read the new value or close the file and re-open it
+               to read the value.
+
+       "edge" ... reads as either "none", "rising", "falling", or
+               "both". Write these strings to select the signal edge(s)
+               that will make poll(2) on the "value" file return.
+
+               This file exists only if the pin can be configured as an
+               interrupt generating input pin.
+
+       "active_low" ... reads as either 0 (false) or 1 (true). Write
+               any nonzero value to invert the value attribute both
+               for reading and writing. Existing and subsequent
+               poll(2) support configuration via the edge attribute
+               for "rising" and "falling" edges will follow this
+               setting.
+
+GPIO controllers have paths like /sys/class/gpio/gpiochip42/ (for the
+controller implementing GPIOs starting at #42) and have the following
+read-only attributes:
+
+    /sys/class/gpio/gpiochipN/
+
+       "base" ... same as N, the first GPIO managed by this chip
+
+       "label" ... provided for diagnostics (not always unique)
+
+       "ngpio" ... how many GPIOs this manges (N to N + ngpio - 1)
+
+Board documentation should in most cases cover what GPIOs are used for
+what purposes. However, those numbers are not always stable; GPIOs on
+a daughtercard might be different depending on the base board being used,
+or other cards in the stack. In such cases, you may need to use the
+gpiochip nodes (possibly in conjunction with schematics) to determine
+the correct GPIO number to use for a given signal.
+
+
+Exporting from Kernel code
+--------------------------
+Kernel code can explicitly manage exports of GPIOs which have already been
+requested using gpio_request():
+
+       /* export the GPIO to userspace */
+       int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
+
+       /* reverse gpio_export() */
+       void gpiod_unexport(struct gpio_desc *desc);
+
+       /* create a sysfs link to an exported GPIO node */
+       int gpiod_export_link(struct device *dev, const char *name,
+                     struct gpio_desc *desc);
+
+       /* change the polarity of a GPIO node in sysfs */
+       int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value);
+
+After a kernel driver requests a GPIO, it may only be made available in
+the sysfs interface by gpiod_export(). The driver can control whether the
+signal direction may change. This helps drivers prevent userspace code
+from accidentally clobbering important system state.
+
+This explicit exporting can help with debugging (by making some kinds
+of experiments easier), or can provide an always-there interface that's
+suitable for documenting as part of a board support package.
+
+After the GPIO has been exported, gpiod_export_link() allows creating
+symlinks from elsewhere in sysfs to the GPIO sysfs node. Drivers can
+use this to provide the interface under their own device in sysfs with
+a descriptive name.
+
+Drivers can use gpiod_sysfs_set_active_low() to hide GPIO line polarity
+differences between boards from user space. Polarity change can be done both
+before and after gpiod_export(), and previously enabled poll(2) support for
+either rising or falling edge will be reconfigured to follow this setting.
index b466974e142fc3e288c993697455b24020ece1ce..ab81013cc3907a45ffb039a1f72a98d83f1b46b8 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 d29dea0f323287a3617205de7f12880e706a0cd8..7b0dcdb57173ce2246303c2f20eec1e94a5c6b8b 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 8002c894c6b052f3dc8443b82a69ec2ba36faae1..31bb6a4029ef84863a8f1341e023ed8961142a23 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 8ef6dbb6a462d707401fe08289dfa51702125789..bbc99c0c1094949d8dd36f68f2516e8fb879b0e4 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 9ca3e74a10e128b4103d00dc8d06c12e6127d4c8..50680a59a2ff9a913e449a1d71ced9fab3004fe4 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 c8c42e64e953b4cd47d23da125ac5e057d1c158d..020cccdbdd0ce9052d26b6dd4cd2bcef8dab7a4e 100644 (file)
@@ -500,7 +500,7 @@ odd-numbered bank is idle, one can see the new value of the pointer P (&B),
 but the old value of the variable B (2).
 
 
-Another example of where data dependency barriers might by required is where a
+Another example of where data dependency barriers might be required is where a
 number is read from memory and then used to calculate the index for an array
 access:
 
@@ -882,12 +882,12 @@ cache it for later use.
 
 Consider:
 
-       CPU 1                   CPU 2
+       CPU 1                   CPU 2
        ======================= =======================
-                               LOAD B
-                               DIVIDE          } Divide instructions generally
-                               DIVIDE          } take a long time to perform
-                               LOAD A
+                               LOAD B
+                               DIVIDE          } Divide instructions generally
+                               DIVIDE          } take a long time to perform
+                               LOAD A
 
 Which might appear as this:
 
@@ -910,13 +910,13 @@ Which might appear as this:
 Placing a read barrier or a data dependency barrier just before the second
 load:
 
-       CPU 1                   CPU 2
+       CPU 1                   CPU 2
        ======================= =======================
-                               LOAD B
-                               DIVIDE
-                               DIVIDE
+                               LOAD B
+                               DIVIDE
+                               DIVIDE
                                <read barrier>
-                               LOAD A
+                               LOAD A
 
 will force any value speculatively obtained to be reconsidered to an extent
 dependent on the type of barrier used.  If there was no change made to the
@@ -1887,8 +1887,8 @@ functions:
      space should suffice for PCI.
 
      [*] NOTE! attempting to load from the same location as was written to may
-        cause a malfunction - consider the 16550 Rx/Tx serial registers for
-        example.
+        cause a malfunction - consider the 16550 Rx/Tx serial registers for
+        example.
 
      Used with prefetchable I/O memory, an mmiowb() barrier may be required to
      force stores to be ordered.
@@ -1955,19 +1955,19 @@ barriers for the most part act at the interface between the CPU and its cache
                                  :
        +--------+    +--------+  :   +--------+    +-----------+
        |        |    |        |  :   |        |    |           |    +--------+
-       |  CPU   |    | Memory |  :   | CPU    |    |           |    |        |
-       |  Core  |--->| Access |----->| Cache  |<-->|           |    |        |
+       |  CPU   |    | Memory |  :   | CPU    |    |           |    |        |
+       |  Core  |--->| Access |----->| Cache  |<-->|           |    |        |
        |        |    | Queue  |  :   |        |    |           |--->| Memory |
-       |        |    |        |  :   |        |    |           |    |        |
-       +--------+    +--------+  :   +--------+    |           |    |        |
+       |        |    |        |  :   |        |    |           |    |        |
+       +--------+    +--------+  :   +--------+    |           |    |        |
                                  :                 | Cache     |    +--------+
                                  :                 | Coherency |
                                  :                 | Mechanism |    +--------+
        +--------+    +--------+  :   +--------+    |           |    |        |
        |        |    |        |  :   |        |    |           |    |        |
        |  CPU   |    | Memory |  :   | CPU    |    |           |--->| Device |
-       |  Core  |--->| Access |----->| Cache  |<-->|           |    |        |
-       |        |    | Queue  |  :   |        |    |           |    |        |
+       |  Core  |--->| Access |----->| Cache  |<-->|           |    |        |
+       |        |    | Queue  |  :   |        |    |           |    |        |
        |        |    |        |  :   |        |    |           |    +--------+
        +--------+    +--------+  :   +--------+    +-----------+
                                  :
@@ -2090,7 +2090,7 @@ CPU's caches by some other cache event:
        p = &v;         q = p;
                        <D:request p>
        <B:modify p=&v> <D:commit p=&v>
-                       <D:read p>
+                       <D:read p>
                        x = *q;
                        <C:read *q>     Reads from v before v updated in cache
                        <C:unbusy>
@@ -2115,7 +2115,7 @@ queue before processing any further requests:
        p = &v;         q = p;
                        <D:request p>
        <B:modify p=&v> <D:commit p=&v>
-                       <D:read p>
+                       <D:read p>
                        smp_read_barrier_depends()
                        <C:unbusy>
                        <C:commit v=2>
index 8b8a0578764112c1dacc4a719f454c676c09100c..3c12d9a7ed00391d5c3f49ef80d7b1ae9abc40fe 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 3f10b39b034639cd558e080d289ed361c7ea63f7..89a8816990ffbdc1b5be616427f8977f802db38a 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 0f54333b0ff2990ce090f88087e17417a894b46c..b6ce00b2be9ae9682c6821bfb70175e42cc75e57 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 1039b68fe9c62aa773f4e90e68613870c0cd9db3..93cb979749860f323bb797ce2adc2923d629e1ed 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 fd1cd8aae4eb584b3b963a884f477f22cd037b2d..16eb314f56cc45ce923d9354960bdf67ea4e6b98 100644 (file)
@@ -146,8 +146,8 @@ On removal:
  1) set the 'list_op_pending' word to the address of the 'lock entry'
     to be removed,
  2) remove the lock entry for this lock from the 'head' list,
2) release the futex lock, and
2) clear the 'lock_op_pending' word.
3) release the futex lock, and
4) clear the 'lock_op_pending' word.
 
 On exit, the kernel will consider the address stored in
 'list_op_pending' and the address of each 'lock word' found by walking
index 414235c1fcfcdd3f4a8ca191df7edf7cf21049ad..45c82fd3e9d39bacaa1febcabba942446c2afda4 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 7b4145d00452f259fe79eff4f400ecfc949a6fd1..a4c33f1a7c6de5dc2207a21bab00846266668f90 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 54d29c1320ed665daa5b73c652bf4b50af63b488..230ce71f4d75529ff4e071ed25e7bf0b4f72cd3c 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 a9248da5cdbc1a0c7b60031f6d0677d726e532ec..ef2ccbf77fa2d4e8b8152d844d3a24de8fb54045 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 8ec2a67c39b7c6a1114e75563fd980717155103f..4cf53e40661392438cf54d0edda07c87f0ac8e4f 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 858aecf21db2c9d449d984af406a838ed5143ed4..a30035dd4c26a49d25db29f2f2f3aecd81e2b606 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 22ff659bc0fb644704933e50cb931d0e2681232b..3c65feb83010133de17382c4fe4f21d7b602ff16 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 41b7ac9884b5ebdeaba602077bebb11efebcc648..f8869410d40ce8ba4eac30d3193f28aedb8383a6 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 5481c8ba34122df7d89ccc16f732174b987c699a..a39d06680e1c1a738437dfafdb36f1fda29b2f66 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 3f85561f4a8bcefab5e154426fcdf254471b5e27..e224dade4db719f74e7c19c4d6b94a10f4a8ff99 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>
@@ -2143,6 +2142,11 @@ L:       linux-usb@vger.kernel.org
 S:     Maintained
 F:     drivers/usb/chipidea/
 
+CHROME HARDWARE PLATFORM SUPPORT
+M:     Olof Johansson <olof@lixom.net>
+S:     Maintained
+F:     drivers/platform/chrome/
+
 CISCO VIC ETHERNET NIC DRIVER
 M:     Christian Benvenuti <benve@cisco.com>
 M:     Sujith Sankar <ssujith@cisco.com>
@@ -2469,7 +2473,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 +2853,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 +3068,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 +3157,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 +4070,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 +4828,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 +4894,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/
@@ -5093,6 +5117,11 @@ F:       drivers/lguest/
 F:     include/linux/lguest*.h
 F:     tools/lguest/
 
+LIBLOCKDEP
+M:     Sasha Levin <sasha.levin@oracle.com>
+S:     Maintained
+F:     tools/lib/lockdep/
+
 LINUX FOR IBM pSERIES (RS/6000)
 M:     Paul Mackerras <paulus@au.ibm.com>
 W:     http://www.ibm.com/linux/ltc/projects/ppc
@@ -5211,6 +5240,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 +6810,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
@@ -7496,9 +7525,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/
@@ -8645,6 +8675,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>
@@ -8941,8 +8972,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.*
 
@@ -8963,8 +8994,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
 
@@ -9503,8 +9534,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 606a66cdcdb88e6e1b7d214dc7f87921c59e025d..2c88e44a1dd4d9dbf726ab1f071db8accb805884 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
-PATCHLEVEL = 12
+PATCHLEVEL = 13
 SUBLEVEL = 0
-EXTRAVERSION =
+EXTRAVERSION = -rc2
 NAME = One Giant Leap for Frogkind
 
 # *DOCUMENTATION*
@@ -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 ded747c7b74c32e66fa681d7e710528c23421f9e..f1cf895c040fb674ed1c07e5cc43459e9244a8f6 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 35a300d4a9fb37f3a08e3f63365c72bcc2a3ecbf..d39dc9b95a2c6810ae920ed9d5dbdadbcbb37de5 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 72dbf235927054145d8c18c14968d452efd2c494..75cb3641ed2f0b507c1bcd6a86cb2d8da2966044 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 6fcd2b5b08f0d5ac4462ba6616a66519c2819d7d..5422a47646fc20add2047b57d41a91593905e242 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 bc2a0daf2d9266f067af47feb0bd469893cc3658..aab14a019c20f82c7cc60084026b120727971214 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 d70408d36677c86d0fbd8ce90530724e54d15a57..f71c3b0ed3606c7fc96ab6ee45b66ba324dc30ee 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 b02b8a282940fd3e64bde0d757fdb4f6a5ba94c7..c2911f5917041abd49dea5f14855ac9691d1aec0 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 52cd2a4a3ff486c8e7acefb35c6e6d24a911f025..453597b91f3a6560320524e4ef7a4338a5ae8e10 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 3c0ce08e5f592d9b779c10a441527cd9061e9c07..dfc8140b908821d46a3d82f30baaa121c1bf64ef 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 84ec46b38f7dc1c39043f8671bc60b3db1a62b9d..0d54650e78fc6b622272d88ac6406273f8e9a282 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 89566b346c0f802bd5bfe0c817bd2fee9bd39d3e..f4c7ab6f43b0dc167bce50125c812e79ebb2d067 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 28e4429596f3f208c1b868887112bad4faa33987..1c8625cb0e253fbc57e1b341fe48867bd7369e96 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 7fa62488bd16791f77b0218451629d749c2e5351..f54bdf658cd0b9ff6b72f71fc40504bd8dff252e 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 d821b17047e0abbe54dd82871d3cd9e324d28df8..c52e7f0ee5f6084bd2c8068a552659f97edd9a8d 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 f2360a74e5d5544983160d951c46bddb98819e0e..1941a07b5811f925aed82e853aab4efb081f74ca 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 d3e52d3fd59299771ed3e90d0fc13f0aca9faa6f..da2d6ec9c37065ca48265597cc6cd8b91f6e2796 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 9e3107cc5ebb45d7dccc2889462f9f10c4b575ec..b20af76f12c1dbf548a27210fdbb196a2c77496d 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 9dbbcb3b914675f80e3e0f097ced7e4545b533f9..99ac36d5de4efd10832804e82509e062606720e2 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 5a0af11b3a61c1b97b5e1da19fe1fb8f2dd15f50..608f2a7fa0a30f415e2bdef4c7424957080b8641 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 c92e389ff2192973f95f51073a5ce50e5f132a3b..f21d61fab6787331d21571958185b637fc601bb7 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 ea3395036556ceea61c74c08452d5e7a7061e566..ee39cee8064caa4e930a06eb2fa35457ad6d390e 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 bd0665cdc840d3e9a79b752ec3a88279ede69344..9c4c189eb22f5a9db2d2ae678756a5241b3e1ee5 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 ffb19b7da999c67722d5690dcfcd6ef0d74123f8..ff3c10721caf67be1873f5c5b88623fc65fd89ea 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 d8b94e1c7fcad001c32142f4e4cc51d3cc8f6a5b..356bb2fdd70567721023b8e0a3fc1d59f2f5d981 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 311b8cfc691488743d178c2dd79eb9f7edafbd81..76ccc6d1f364d67ca8c03c859171f113da5e23ce 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 5ede5460c80692502730d69510a57975b386fe8d..2ee0c9bfd0325537a5d9299649abac4992b722f5 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 d9f8249aa66e0033fa95c5c66b04d6e5f8ef641b..3942634f805ade1545d30dd066d2ba88cdec3cac 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 ebc313a9f5b248df51352f909697afca5cc9e88f..3dd6ed941464ac663e76330b43ee0e3d588cdf87 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 da8ca7941e6741493dcfa0a71d62961d754571ef..b0467229a5c45e9c10eb1cc2884b84b33ef2e603 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 b204657993aab2fca0127365b74aaced843b9a2e..1cf51c280f285ea3f38ee733ff811727ae2a63c5 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 edf56f4749e13420dcdad51fe25ba58a2ee18ab3..a098d7c05e967461cc200fbb52d1acb51dcee966 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 4fb2d6f655bdf4162d40b6d72ae6f329b0b32ec0..bcf662d21a57b38fbb6c34ee26033fcd33b0e148 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 115ad96480e66d7e4399292bfbd64b52a16c4729..cbf755e32a03f3c19296b552e3f767865fbacc86 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 36a9f20c21a356988e09287939c1a1dc9f5ae211..81208bfd9dcbc460c9875ad509bb0cb11348c07e 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 2d50a4cdd7f3dacb8828aaa5c40133a2fc6c7681..45be2167201183172695d0a788c0b20b87ec7ce2 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 c242ef07ba704d5437edeacd7dddf7e414431876..8004b4fa64615cd3c570219e8e5575848e4c99ac 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 eb1c2ee5eaf0a4e0b8811092568943ed96be5388..42b05046fad9f13b3cffecc1cb041e8febed3840 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 1ab386bb5da895bd4e876b1d78977e684c83f3fe..6994c188dc88c82bbc256f6835c6e54b6107545a 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 603d661b445d4a2690c7bcb5dbd9cb6d282cc0e6..c1f1a7eee953de4378b1f74bd4907c969f96dceb 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 987429436171f0dd2ac8bb1bd0b64e548fc1e722..7e6c64ed966d66b4bfe365db5f85186a88b49a24 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 03febf85fd2f055373259c19e01c5ad9d8148e65..4718ec4a4dbfef5e2a7273726cb6a8f0750623e3 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 1e12aeff403b018cf174ff1b710af391f970c997..aa537ed13f0a578ade79e74c62a56302f1d65437 100644 (file)
@@ -85,6 +85,8 @@
                        reg = <0x7e205000 0x1000>;
                        interrupts = <2 21>;
                        clocks = <&clk_i2c>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
                        status = "disabled";
                };
 
@@ -93,6 +95,8 @@
                        reg = <0x7e804000 0x1000>;
                        interrupts = <2 21>;
                        clocks = <&clk_i2c>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
                        status = "disabled";
                };
 
index dc259e8b8a73a8630dc9698295e662f0ab66e43d..9b186ac06c8ba2fbeff30f9bd58f6ca797e1b05d 100644 (file)
                i2c2_bus: i2c2-bus {
                        samsung,pin-pud = <0>;
                };
+
+               max77686_irq: max77686-irq {
+                       samsung,pins = "gpx3-2";
+                       samsung,pin-function = <0>;
+                       samsung,pin-pud = <0>;
+                       samsung,pin-drv = <0>;
+               };
        };
 
        i2c@12C60000 {
 
                max77686@09 {
                        compatible = "maxim,max77686";
+                       interrupt-parent = <&gpx3>;
+                       interrupts = <2 0>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&max77686_irq>;
+                       wakeup-source;
                        reg = <0x09>;
 
                        voltage-regulators {
index 139b40cc3a23e63087fc6154ca6e7f79ed828fae..2ccbb57fbfa87d8a86aef40d16e756aa78df6d3b 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 bc22557d7a6a977b3cce656c115eb31e1d060398..b90045a8f8e3749f849c3e0d790e99565a313094 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 6aad34ad9517f37424071dcac4859c400a93d1f5..ed14aeac056679059d823b8502a122e28ab1ddaa 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 f4dcff3a9969a053d0a7c136015c94526a739cb8..4bcdd3ad15e524d95cb553b47fc6f61e5eef9a3a 100644 (file)
 
                        usbphy0: usbphy@0 {
                                compatible = "usb-nop-xceiv";
-                               clocks = <&clks 124>;
+                               clocks = <&clks 75>;
                                clock-names = "main_clk";
                                status = "okay";
                        };
index 59154dc15fe4ee441c80fd17d9c9141a16ee5e60..fb28b2ecb1db37a28a9effbd7bea591ace78554a 100644 (file)
                                        clocks = <&clks 197>, <&clks 3>,
                                                 <&clks 197>, <&clks 107>,
                                                 <&clks 0>,   <&clks 118>,
-                                                <&clks 62>,  <&clks 139>,
+                                                <&clks 0>,  <&clks 139>,
                                                 <&clks 0>;
                                        clock-names = "core",  "rxtx0",
                                                      "rxtx1", "rxtx2",
index b0ee342598f070b508770074d6f474589503628e..68221fab978d40a2e92c5b089e0e4fd1494e39e4 100644 (file)
@@ -13,7 +13,7 @@
         * they probably share the same GPIO IRQ
         * REVISIT: Add timing support from slls644g.pdf
         */
-       8250@3,0 {
+       uart@3,0 {
                compatible = "ns16550a";
                reg = <3 0 0x100>;
                bank-width = <2>;
index a2bfcde858a6ec68f96fd123c515747f3c053004..d0c5b37e248c76734d8c58bc54660f65fc6f877a 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/pinctrl/omap.h>
 
 #include "skeleton.dtsi"
@@ -21,6 +22,8 @@
                serial0 = &uart1;
                serial1 = &uart2;
                serial2 = &uart3;
+               i2c0 = &i2c1;
+               i2c1 = &i2c2;
        };
 
        cpus {
                ranges;
                ti,hwmods = "l3_main";
 
+               aes: aes@480a6000 {
+                       compatible = "ti,omap2-aes";
+                       ti,hwmods = "aes";
+                       reg = <0x480a6000 0x50>;
+                       dmas = <&sdma 9 &sdma 10>;
+                       dma-names = "tx", "rx";
+               };
+
+               hdq1w: 1w@480b2000 {
+                       compatible = "ti,omap2420-1w";
+                       ti,hwmods = "hdq1w";
+                       reg = <0x480b2000 0x1000>;
+                       interrupts = <58>;
+               };
+
+               mailbox: mailbox@48094000 {
+                       compatible = "ti,omap2-mailbox";
+                       ti,hwmods = "mailbox";
+                       reg = <0x48094000 0x200>;
+                       interrupts = <26>;
+               };
+
                intc: interrupt-controller@1 {
                        compatible = "ti,omap2-intc";
                        interrupt-controller;
@@ -63,6 +88,7 @@
 
                sdma: dma-controller@48056000 {
                        compatible = "ti,omap2430-sdma", "ti,omap2420-sdma";
+                       ti,hwmods = "dma";
                        reg = <0x48056000 0x1000>;
                        interrupts = <12>,
                                     <13>,
                        #dma-requests = <64>;
                };
 
+               i2c1: i2c@48070000 {
+                       compatible = "ti,omap2-i2c";
+                       ti,hwmods = "i2c1";
+                       reg = <0x48070000 0x80>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <56>;
+                       dmas = <&sdma 27 &sdma 28>;
+                       dma-names = "tx", "rx";
+               };
+
+               i2c2: i2c@48072000 {
+                       compatible = "ti,omap2-i2c";
+                       ti,hwmods = "i2c2";
+                       reg = <0x48072000 0x80>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <57>;
+                       dmas = <&sdma 29 &sdma 30>;
+                       dma-names = "tx", "rx";
+               };
+
+               mcspi1: mcspi@48098000 {
+                       compatible = "ti,omap2-mcspi";
+                       ti,hwmods = "mcspi1";
+                       reg = <0x48098000 0x100>;
+                       interrupts = <65>;
+                       dmas = <&sdma 35 &sdma 36 &sdma 37 &sdma 38
+                               &sdma 39 &sdma 40 &sdma 41 &sdma 42>;
+                       dma-names = "tx0", "rx0", "tx1", "rx1",
+                                   "tx2", "rx2", "tx3", "rx3";
+               };
+
+               mcspi2: mcspi@4809a000 {
+                       compatible = "ti,omap2-mcspi";
+                       ti,hwmods = "mcspi2";
+                       reg = <0x4809a000 0x100>;
+                       interrupts = <66>;
+                       dmas = <&sdma 43 &sdma 44 &sdma 45 &sdma 46>;
+                       dma-names = "tx0", "rx0", "tx1", "rx1";
+               };
+
+               rng: rng@480a0000 {
+                       compatible = "ti,omap2-rng";
+                       ti,hwmods = "rng";
+                       reg = <0x480a0000 0x50>;
+                       interrupts = <36>;
+               };
+
+               sham: sham@480a4000 {
+                       compatible = "ti,omap2-sham";
+                       ti,hwmods = "sham";
+                       reg = <0x480a4000 0x64>;
+                       interrupts = <51>;
+                       dmas = <&sdma 13>;
+                       dma-names = "rx";
+               };
+
                uart1: serial@4806a000 {
                        compatible = "ti,omap2-uart";
                        ti,hwmods = "uart1";
+                       reg = <0x4806a000 0x2000>;
+                       interrupts = <72>;
+                       dmas = <&sdma 49 &sdma 50>;
+                       dma-names = "tx", "rx";
                        clock-frequency = <48000000>;
                };
 
                uart2: serial@4806c000 {
                        compatible = "ti,omap2-uart";
                        ti,hwmods = "uart2";
+                       reg = <0x4806c000 0x400>;
+                       interrupts = <73>;
+                       dmas = <&sdma 51 &sdma 52>;
+                       dma-names = "tx", "rx";
                        clock-frequency = <48000000>;
                };
 
                uart3: serial@4806e000 {
                        compatible = "ti,omap2-uart";
                        ti,hwmods = "uart3";
+                       reg = <0x4806e000 0x400>;
+                       interrupts = <74>;
+                       dmas = <&sdma 53 &sdma 54>;
+                       dma-names = "tx", "rx";
                        clock-frequency = <48000000>;
                };
 
index c8f9c55169ead249c9f95add2f15ab99527dd01c..60c605de22ddcdfb9f7220669c12c443a4c218fa 100644 (file)
                        dma-names = "tx", "rx";
                };
 
+               msdi1: mmc@4809c000 {
+                       compatible = "ti,omap2420-mmc";
+                       ti,hwmods = "msdi1";
+                       reg = <0x4809c000 0x80>;
+                       interrupts = <83>;
+                       dmas = <&sdma 61 &sdma 62>;
+                       dma-names = "tx", "rx";
+               };
+
                timer1: timer@48028000 {
                        compatible = "ti,omap2420-timer";
                        reg = <0x48028000 0x400>;
                        ti,hwmods = "timer1";
                        ti,timer-alwon;
                };
+
+               wd_timer2: wdt@48022000 {
+                       compatible = "ti,omap2-wdt";
+                       ti,hwmods = "wd_timer2";
+                       reg = <0x48022000 0x80>;
+               };
        };
 };
+
+&i2c1 {
+       compatible = "ti,omap2420-i2c";
+};
+
+&i2c2 {
+       compatible = "ti,omap2420-i2c";
+};
index c535a5a2b27f9aa95b313c768893520b82d3a6c7..d624345666f56a1468c9e628ae1f3b971fb5d43a 100644 (file)
                        dma-names = "tx", "rx";
                };
 
+               mmc1: mmc@4809c000 {
+                       compatible = "ti,omap2-hsmmc";
+                       reg = <0x4809c000 0x200>;
+                       interrupts = <83>;
+                       ti,hwmods = "mmc1";
+                       ti,dual-volt;
+                       dmas = <&sdma 61>, <&sdma 62>;
+                       dma-names = "tx", "rx";
+               };
+
+               mmc2: mmc@480b4000 {
+                       compatible = "ti,omap2-hsmmc";
+                       reg = <0x480b4000 0x200>;
+                       interrupts = <86>;
+                       ti,hwmods = "mmc2";
+                       dmas = <&sdma 47>, <&sdma 48>;
+                       dma-names = "tx", "rx";
+               };
+
                timer1: timer@49018000 {
                        compatible = "ti,omap2420-timer";
                        reg = <0x49018000 0x400>;
                        ti,hwmods = "timer1";
                        ti,timer-alwon;
                };
+
+               mcspi3: mcspi@480b8000 {
+                       compatible = "ti,omap2-mcspi";
+                       ti,hwmods = "mcspi3";
+                       reg = <0x480b8000 0x100>;
+                       interrupts = <91>;
+                       dmas = <&sdma 15 &sdma 16 &sdma 23 &sdma 24>;
+                       dma-names = "tx0", "rx0", "tx1", "rx1";
+               };
+
+               usb_otg_hs: usb_otg_hs@480ac000 {
+                       compatible = "ti,omap2-musb";
+                       ti,hwmods = "usb_otg_hs";
+                       reg = <0x480ac000 0x1000>;
+                       interrupts = <93>;
+               };
+
+               wd_timer2: wdt@49016000 {
+                       compatible = "ti,omap2-wdt";
+                       ti,hwmods = "wd_timer2";
+                       reg = <0x49016000 0x80>;
+               };
        };
 };
+
+&i2c1 {
+       compatible = "ti,omap2430-i2c";
+};
+
+&i2c2 {
+       compatible = "ti,omap2430-i2c";
+};
index fb1b2ec8eaa99f4b366ccb29a5ed28f78a109571..4217096ee6777bd292c208a23ba02ad86a2af8c9 100644 (file)
                interrupts = <11>;
        };
 
+       charger: bci {
+               compatible = "ti,twl4030-bci";
+               interrupts = <9>, <2>;
+               bci3v1-supply = <&vusb3v1>;
+       };
+
        watchdog {
                compatible = "ti,twl4030-wdt";
        };
index 8e1a0245907f85be1a460bfa785f744daf285d6f..41bca32409fce81358c3b5c35bc081bcc28e7c76 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 002a1ceadceb635f65b1e5523ecdb7f4e74710a6..23591dba47a04ac2fe5efdd55f493551cfd63538 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 863cd84eb1a24955e9af8253dcfa8f0b710c95ae..e701a4d9aa591f02f729075e0727468fec8d69e5 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 9b28f1243bdc1d96c2be3dac0ad947a72a61a264..240b29ef17db9772af6abc4855b90c7c16621e81 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 122f86d8c991d73e587c786eba41bf85a6ec5a21..250760e081039542dbcdae191b1b5321921a1d3d 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 d070741b2b37b24c8cc1fe4284ecdccc6eb306ba..3c597c222ef278a8eb170a4169b5e54dcc158ac0 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 64e96960de297c850c480f1df3be1dc331ee891a..1d3153c7eb41c54d1e97fcd6078057fe5735a7dd 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 a2f43ddcc3004aeaa342666c4973277f076390cd..661da11f76f4a5eca006850396c7760034fab48f 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 a464e8d7b6c58f77c5f57fdda4202587f2331962..0fa90c962ac831329df43a156c5b0583732099ac 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 7d22517d8071192e467a6653a177ab08742d2650..8a6f6db14ee412ae06dc18bd4027d9652321fc0b 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 9b28c41f4ba916a569bf1f105064f092ab434dda..77de4a41cc5045bd3b778f98396a454d23767467 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 4dd21457ef9d2be8b1c94cac7eea97c8ef8cc1f6..9ecccc865046a2c257277cd03a8f607ed5e0217d 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 943504f53f579e58c878165c595d5a110dae00ed..78a779361682adedae9acd430998c227842425bb 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 39c54cfa03e9b103ef39982a43bac74d5436b791..4f9503908dca4a7dc536324514801bcbd03d32fd 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 df5e13d64f2c02b28963c23b319a7634cf5dacc8..71a06b293489ddffd1b4177eca38572501c0ce36 100644 (file)
@@ -140,12 +140,6 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
                                    struct user_vfp_exc __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
index d7ab99a0c9ebe2499b2c4defca40c0cb4a893358..1317ee40f4dfd6051daa9efaa77e52dfcee37fe5 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 359a7b50b158c4987edf6cc0582174a4cf678473..75579a9d6f76cba3cc84ad61d01ed103269378bd 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 c1ee007523d78dd25b1dd21661af605da4aa7ef3..c498b60c0505c35ed1da467923795975075e7455 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 7801866e626a2a1a4631d9e3e3fbd3c27ddda429..11d59b32fb8dca45613ed00fb225a72359c19216 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
@@ -643,8 +644,12 @@ ARM_BE8(rev16      ip, ip)
        ldrcc   r7, [r4], #4    @ use branch for delay slot
        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 6125f259b7b5359072b0cd7a07e122fcd2bda4bd..dbf0923e8d76bda9392b902e0c8e500025d70402 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 ebf5015508b525c052ddfed5c4ea2ea0a0ed969e..466bd299b1a8aad54949364d976d9c5430c2375e 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 d99bee4950e50a6c26e428acc710448fba1a726f..789bca9e64a7fdeabe091df30db44daf8b4b1b40 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 aea7ccb8d3970f236c34feb7d668c67bcb925b01..2a700e00528d0a3cc9d78a58f7d38dddf2297611 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 db9cf692d4dded3e2a6cc7e5622ba90ee5bef2e8..78c0885d65015df28ea7554a5e97a85b588e9fb1 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 },
@@ -194,6 +298,13 @@ static const struct coproc_reg cp15_regs[] = {
        { CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, access_dcsw},
        { 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.
         */
@@ -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 cf93472b9dd60daf3da620cf3a44a9ff65a6eac6..bb0cac1410ccf3f2bf6c6e4ff8436e8750036357 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 bdede9e7da516a43b5a3d681850727860f0534ab..d6c005283678fe5061a50cc8f5efd1febcc0f27b 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 152d03612181d16d5fef5e1e84da8d2c178fbf58..20f8d97904afafc4ec9814b9d74479a7233088ab 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 df4c82d47ad7101cf2f87028d39471cd6d63d7df..a92079011a836974a9188ab8db1fa75f2bc5403a 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 0c25d9487d5382d2a19a1b3399398244f3718866..4cb5a93182e9283f78f5ddd7c6b51c38a51c783b 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 b0de86b56c13006a189efba24967b81d162a56d6..580906989db1091eb034ada5cc60570505de8cd1 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 86a693a02ba3a78e4288b94c63f3b1fba77a6b38..0881bf169fbce5cf09db6da3ff0996df6cf989cb 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 c02ba4af599f417113fdb2c260270ae7162575e6..f558c073c02378a449a05d337d47a8161ae5c51d 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 e0c68d5bb7dc25dd3fa93dc0fa1b3899f5b09019..52886b89706caf466b1cc6c6586db70a7d9d962e 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 c1b737097c9543faf67c4ed78f2617e3add0606f..90aab2d5a07f3aed6acc2ed8788105b877756fe6 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 f8629a3fa2452791b6215c19f77124813e1d3f5f..d6a1fa85371d3ee732f4048298532dab817478fc 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 1f3867a17a289beee4bb1a44ae64ca3bc96b044d..23ba1d8a1531ca5f123f94591290450a0a403be6 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 90d455d294a1814e7e72722b5614b4d15548c298..7eccb0fc57bc080c3eaee8c8f830a2a7fb127cad 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 e9bf0b8f40eb745d317e2d1d50c500eea1491aa4..9405aa08b10498430b097594865c95976297c6ba 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 2d895a297739d85d4302ec8e9ec2e01cfb4f1c69..388ec3aec4b95edc8e63f8907a09fd66b2889a15 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 88995af09c043abf6198124d1c651d60ac03b0ff..0750ffb7e6b16d7a52dd636475226b9b9035b301 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 916e5a1429171bd39835da54b02fa444b1941905..e8a2e075a1b888262077e46457774679d51776e9 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 0b153c87521d8e73feaf6b9bc53291431bc6b7a8..f4f8735315dafd8247f686e756423d8b9608a013 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 8b4942cbb6d9a62ffdb1f88e9676670817b7a107..2f931915c80c8d2f9a6c9059cf2e6eb07d2093a7 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 dc6e2f5f804dcc26471daf42ecd4ef1e454656fa..26dee3ce9397a0cf41705d0fbafe6ee87b0a9fbc 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 d374b87c045914348349ef721feadd9e87170921..0151bcf6163cddd349c5d6f8e88198c3831bf3b9 100644 (file)
 #define AT91SAM9N12_BASE_USART2        0xf8024000
 #define AT91SAM9N12_BASE_USART3        0xf8028000
 
+/*
+ * System Peripherals
+ */
+#define AT91SAM9N12_BASE_RTC   0xfffffeb0
+
 /*
  * Internal Memory.
  */
index c75ee19b58d3de69c79e81e312c416fbd6071ef0..2fc76c49e97cf4152c9427f62a39bad4e71b51d4 100644 (file)
 #define AT91SAM9X5_BASE_USART1 0xf8020000
 #define AT91SAM9X5_BASE_USART2 0xf8024000
 
+/*
+ * System Peripherals
+ */
+#define AT91SAM9X5_BASE_RTC    0xfffffeb0
+
 /*
  * Internal Memory.
  */
index 31096a8aaf1d507287d62f6ef8250096f7ee09ea..25613d8c6dcd6687dd0b896883aedbe52f3461c6 100644 (file)
 #define SAMA5D3_BASE_USART2    0xf8020000
 #define SAMA5D3_BASE_USART3    0xf8024000
 
+/*
+ * System Peripherals
+ */
+#define SAMA5D3_BASE_RTC       0xfffffeb0
+
 /*
  * Internal Memory
  */
index 401279715ab19b8ec694463d442af33b93aff827..3ea86428ee0964f11d0955a90d0348626e68da58 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 40f15f133c55c3646d5eeac3ef3eec7764cae760..d1f45af7a530e9abc33d794fcc6cf52010cfc52b 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 df16cb88a26b643814af5385affb94a7a8220882..e0af0eccde8fbe0d9f54abaeb339ee974db4c2d2 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 f4a6c18912ea50abaac233fedf26cef69af06724..e08a8684ead2fed54938bcfeaaac7fe3b5b0efb2 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 9cc32c283b8b90af050de90411a9b1d168b3b050..987605b78556f9e8fa16b1e9b1b278ad0abe4d1c 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 44b20191a9fed5359ed0b24344e0aeb15430d210..13d0801fd6b170e155dfe3d4cf8c069522c4baf7 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 cd0f58730c2ba63234fb5e673156c10214300a80..7aa105b1fd0f7553170134c9a8dae654c931784d 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 d84360148100831265561696b1e661f16a035361..41c7c961579133d9a80070a2c46ce56c07f1d8d9 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 08332d84144052899382d6d2543dc4d754779e6f..0aded64a9ebcd68a4213620ace8b5b2f81b53121 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 bbe1f5bb799c1a0db3cd403f01767b2a7af4471b..1789e2b3190389f287a4ce79a747b17c1072a48d 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 d756d91fd74163852b810a0b0d578eaac5236552..04cfd0fcb0e56db864d28f4240b2341ae20927b0 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 f6640b6a7b3128a7ca6a6d8d31578419ea1be431..61364050fccdce42b477f2a7a4aaafddf136f41a 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 7cbe22d0c6e9c8ae4060e71c06d2c7eb73c0d389..24a7899e36a8abed143d2bfdadcf854e0970a14c 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 19bb6441a7d4aaddb106afa71634606acfae7466..c5f95674e9b72c8caa61052a25c7e90521b3ce42 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 45303bd629022d66ef167d0830f5c6088c5c44d8..639a3dfb00923317871724f1848ace2d9c7e0125 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 e805ac273e9c6cc952e400a4f542f9727881650c..592ddbe031ac714bf7d2d0f0519ba38a3957d0ee 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 b726cb1c5fdd638326148cb4ce66783f69dd7659..ac504b67326b12b51ba9c1ccea470b85e74b1dd3 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 0910761e8280894918927554598c1f4d1d1f4b79..8825d1217d189e7d026dff24f7b2e41af54fde4a 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 4754373e7e7d3a97b29e5af8a143bb27379bb9a5..45f7f4e0a447c517bb9b731eb402e33ea3083f43 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 e6edcd38b282fdaa74027731841c187ed93cda4d..5e3027d3692f8b02c8f3cb327f935520cdce29f3 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 1df6e7602cadb75dac78a961352624b8f855d73f..4fc0a195de0103e865b4827af89dd101f6336241 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 6d3782d85a9ff6d2db65a71de2632cc0b1151b33..a86fd0ed775788012197270c96ea8190282aed47 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 489495976fcd794e211d10c82e59e087bd40b79c..8e3e4331c380dabd5c3a818626b4eabc7922776b 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 a7ce69286688434e0e195e3c8a2e24728ff6dc60..d68909b095f1c06b135bac8e07dee46408dd5d40 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 e15ac005ef17d02a102ad87def740ef1211d53d0..adcef406ff0abdc5a1695cac7f9cb04dc6ac542e 100644 (file)
@@ -19,11 +19,11 @@ secure-common                               = omap-smc.o omap-secure.o
 
 obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common)
 obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common)
-obj-$(CONFIG_ARCH_OMAP4) += prm44xx.o $(hwmod-common) $(secure-common)
+obj-$(CONFIG_ARCH_OMAP4) += $(hwmod-common) $(secure-common)
 obj-$(CONFIG_SOC_AM33XX) += irq.o $(hwmod-common)
-obj-$(CONFIG_SOC_OMAP5)         += prm44xx.o $(hwmod-common) $(secure-common)
+obj-$(CONFIG_SOC_OMAP5)         += $(hwmod-common) $(secure-common)
 obj-$(CONFIG_SOC_AM43XX) += $(hwmod-common) $(secure-common)
-obj-$(CONFIG_SOC_DRA7XX) += prm44xx.o $(hwmod-common) $(secure-common)
+obj-$(CONFIG_SOC_DRA7XX) += $(hwmod-common) $(secure-common)
 
 ifneq ($(CONFIG_SND_OMAP_SOC_MCBSP),)
 obj-y += mcbsp.o
@@ -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 33d159e2386e6a86801d7decced3522125c3b8fb..8dd0ec858cf1cc71372cb1e9d97d23232f7ed0de 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 87e41a8b8d4666059c43a49c2f4ea953b3327fb2..f7808349a7346642a58994c2b5a7b43f3dddb5da 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 a516c1bda1418b05612a529ff1673411afe8d8f5..d6ed819ff15ce111faaff0cfa825dc0b70ad55ee 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 ba8342fef799ee10c3248bc75188bad47b5971bf..119efaf5808ab3df5291969f311e555704e84ee2 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 03a2829beb8e4c69144dfd5e5eb639189b836b02..3b05aea56d1f5636d5ff2e154513cf22349b3eec 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 b237950eb8a319f9fb879d051245cc7c6fea6968..ec0dc0b1755ef97c32424b8a10db8a878e82c15c 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 f7644febee81d7d41ae2cfc01e1fc362b972de02..e30ef6797c6311798cbb92b4521c56306b7fdc67 100644 (file)
@@ -299,7 +299,6 @@ struct omap_sdrc_params;
 extern void omap_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
                                      struct omap_sdrc_params *sdrc_cs1);
 struct omap2_hsmmc_info;
-extern int omap4_twl6030_hsmmc_init(struct omap2_hsmmc_info *controllers);
 extern void omap_reserve(void);
 
 struct omap_hwmod;
index a4e536b11ec9a997d8e640ff44745e6759c031fa..58347bb874a01dcd4d203f4f191712d473a338a3 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "soc.h"
 #include "iomap.h"
-#include "mux.h"
 #include "control.h"
 #include "display.h"
 #include "prm.h"
@@ -102,90 +101,13 @@ static const struct omap_dss_hwmod_data omap4_dss_hwmod_data[] __initconst = {
        { "dss_hdmi", "omapdss_hdmi", -1 },
 };
 
-static void __init omap4_tpd12s015_mux_pads(void)
-{
-       omap_mux_init_signal("hdmi_cec",
-                       OMAP_PIN_INPUT_PULLUP);
-       omap_mux_init_signal("hdmi_ddc_scl",
-                       OMAP_PIN_INPUT_PULLUP);
-       omap_mux_init_signal("hdmi_ddc_sda",
-                       OMAP_PIN_INPUT_PULLUP);
-}
-
-static void __init omap4_hdmi_mux_pads(enum omap_hdmi_flags flags)
-{
-       u32 reg;
-       u16 control_i2c_1;
-
-       /*
-        * CONTROL_I2C_1: HDMI_DDC_SDA_PULLUPRESX (bit 28) and
-        * HDMI_DDC_SCL_PULLUPRESX (bit 24) are set to disable
-        * internal pull up resistor.
-        */
-       if (flags & OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP) {
-               control_i2c_1 = OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_1;
-               reg = omap4_ctrl_pad_readl(control_i2c_1);
-               reg |= (OMAP4_HDMI_DDC_SDA_PULLUPRESX_MASK |
-                       OMAP4_HDMI_DDC_SCL_PULLUPRESX_MASK);
-                       omap4_ctrl_pad_writel(reg, control_i2c_1);
-       }
-}
-
-static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes)
-{
-       u32 enable_mask, enable_shift;
-       u32 pipd_mask, pipd_shift;
-       u32 reg;
-
-       if (dsi_id == 0) {
-               enable_mask = OMAP4_DSI1_LANEENABLE_MASK;
-               enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT;
-               pipd_mask = OMAP4_DSI1_PIPD_MASK;
-               pipd_shift = OMAP4_DSI1_PIPD_SHIFT;
-       } else if (dsi_id == 1) {
-               enable_mask = OMAP4_DSI2_LANEENABLE_MASK;
-               enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT;
-               pipd_mask = OMAP4_DSI2_PIPD_MASK;
-               pipd_shift = OMAP4_DSI2_PIPD_SHIFT;
-       } else {
-               return -ENODEV;
-       }
-
-       reg = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY);
-
-       reg &= ~enable_mask;
-       reg &= ~pipd_mask;
-
-       reg |= (lanes << enable_shift) & enable_mask;
-       reg |= (lanes << pipd_shift) & pipd_mask;
-
-       omap4_ctrl_pad_writel(reg, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY);
-
-       return 0;
-}
-
-int __init omap_hdmi_init(enum omap_hdmi_flags flags)
-{
-       if (cpu_is_omap44xx()) {
-               omap4_hdmi_mux_pads(flags);
-               omap4_tpd12s015_mux_pads();
-       }
-
-       return 0;
-}
-
 static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask)
 {
-       if (cpu_is_omap44xx())
-               return omap4_dsi_mux_pads(dsi_id, lane_mask);
-
        return 0;
 }
 
 static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask)
 {
-       if (cpu_is_omap44xx())
-               omap4_dsi_mux_pads(dsi_id, 0);
 }
 
 static int omap_dss_set_min_bus_tput(struct device *dev, unsigned long tput)
index ef990118d32b2f4205c25c58aec555465096aa39..2757504a13c42ae11319f9f72f4fa6ba50babd29 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 81de56251955a7bf82b7f4ce3b03ffcd3333fd5e..d24926e6340fa714cf0aeacca14a6578e5b481a4 100644 (file)
@@ -1501,6 +1501,22 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
                return ret;
        }
 
+       /*
+        * For some GPMC devices we still need to rely on the bootloader
+        * timings because the devices can be connected via FPGA. So far
+        * the list is smc91x on the omap2 SDP boards, and 8250 on zooms.
+        * REVISIT: Add timing support from slls644g.pdf and from the
+        * lan91c96 manual.
+        */
+       if (of_device_is_compatible(child, "ns16550a") ||
+           of_device_is_compatible(child, "smsc,lan91c94") ||
+           of_device_is_compatible(child, "smsc,lan91c111")) {
+               dev_warn(&pdev->dev,
+                        "%s using bootloader timings on CS%d\n",
+                        child->name, cs);
+               goto no_timings;
+       }
+
        /*
         * FIXME: gpmc_cs_request() will map the CS to an arbitary
         * location in the gpmc address space. When booting with
@@ -1529,6 +1545,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
        gpmc_read_timings_dt(child, &gpmc_t);
        gpmc_cs_set_timings(cs, &gpmc_t);
 
+no_timings:
        if (of_platform_device_create(child, NULL, &pdev->dev))
                return 0;
 
@@ -1541,42 +1558,6 @@ err:
        return ret;
 }
 
-/*
- * REVISIT: Add timing support from slls644g.pdf
- */
-static int gpmc_probe_8250(struct platform_device *pdev,
-                               struct device_node *child)
-{
-       struct resource res;
-       unsigned long base;
-       int ret, cs;
-
-       if (of_property_read_u32(child, "reg", &cs) < 0) {
-               dev_err(&pdev->dev, "%s has no 'reg' property\n",
-                       child->full_name);
-               return -ENODEV;
-       }
-
-       if (of_address_to_resource(child, 0, &res) < 0) {
-               dev_err(&pdev->dev, "%s has malformed 'reg' property\n",
-                       child->full_name);
-               return -ENODEV;
-       }
-
-       ret = gpmc_cs_request(cs, resource_size(&res), &base);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs);
-               return ret;
-       }
-
-       if (of_platform_device_create(child, NULL, &pdev->dev))
-               return 0;
-
-       dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
-
-       return -ENODEV;
-}
-
 static int gpmc_probe_dt(struct platform_device *pdev)
 {
        int ret;
@@ -1618,10 +1599,9 @@ static int gpmc_probe_dt(struct platform_device *pdev)
                else if (of_node_cmp(child->name, "onenand") == 0)
                        ret = gpmc_probe_onenand_child(pdev, child);
                else if (of_node_cmp(child->name, "ethernet") == 0 ||
-                        of_node_cmp(child->name, "nor") == 0)
+                        of_node_cmp(child->name, "nor") == 0 ||
+                        of_node_cmp(child->name, "uart") == 0)
                        ret = gpmc_probe_generic_child(pdev, child);
-               else if (of_node_cmp(child->name, "8250") == 0)
-                       ret = gpmc_probe_8250(pdev, child);
 
                if (WARN(ret < 0, "%s: probing gpmc child %s failed\n",
                         __func__, child->full_name))
index 8cc7d331437d844a3b0ba5b3d2afb844b2de5d06..3e97c6c8ecf139781c7f0d03a68583d5ff81ea2b 100644 (file)
@@ -76,6 +76,13 @@ static inline void omap_barrier_reserve_memblock(void)
 { }
 #endif
 
+#ifdef CONFIG_SOC_HAS_REALTIME_COUNTER
 void set_cntfreq(void);
+#else
+static inline void set_cntfreq(void)
+{
+}
+#endif
+
 #endif /* __ASSEMBLER__ */
 #endif /* OMAP_ARCH_OMAP_SECURE_H */
index 57911430324e30cdfdfb1408d0272c8b6bf0dde4..b39efd46abf991827169a6c17a6aa91f3dfeb39f 100644 (file)
@@ -35,7 +35,6 @@
 #include "iomap.h"
 #include "common.h"
 #include "mmc.h"
-#include "hsmmc.h"
 #include "prminst44xx.h"
 #include "prcm_mpu44xx.h"
 #include "omap4-sar-layout.h"
@@ -284,59 +283,3 @@ skip_errata_init:
        omap_wakeupgen_init();
        irqchip_init();
 }
-
-#if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
-static int omap4_twl6030_hsmmc_late_init(struct device *dev)
-{
-       int irq = 0;
-       struct platform_device *pdev = container_of(dev,
-                               struct platform_device, dev);
-       struct omap_mmc_platform_data *pdata = dev->platform_data;
-
-       /* Setting MMC1 Card detect Irq */
-       if (pdev->id == 0) {
-               irq = twl6030_mmc_card_detect_config();
-               if (irq < 0) {
-                       dev_err(dev, "%s: Error card detect config(%d)\n",
-                               __func__, irq);
-                       return irq;
-               }
-               pdata->slots[0].card_detect_irq = irq;
-               pdata->slots[0].card_detect = twl6030_mmc_card_detect;
-       }
-       return 0;
-}
-
-static __init void omap4_twl6030_hsmmc_set_late_init(struct device *dev)
-{
-       struct omap_mmc_platform_data *pdata;
-
-       /* dev can be null if CONFIG_MMC_OMAP_HS is not set */
-       if (!dev) {
-               pr_err("Failed %s\n", __func__);
-               return;
-       }
-       pdata = dev->platform_data;
-       pdata->init =   omap4_twl6030_hsmmc_late_init;
-}
-
-int __init omap4_twl6030_hsmmc_init(struct omap2_hsmmc_info *controllers)
-{
-       struct omap2_hsmmc_info *c;
-
-       omap_hsmmc_init(controllers);
-       for (c = controllers; c->mmc; c++) {
-               /* pdev can be null if CONFIG_MMC_OMAP_HS is not set */
-               if (!c->pdev)
-                       continue;
-               omap4_twl6030_hsmmc_set_late_init(&c->pdev->dev);
-       }
-
-       return 0;
-}
-#else
-int __init omap4_twl6030_hsmmc_init(struct omap2_hsmmc_info *controllers)
-{
-       return 0;
-}
-#endif
index b69dd9abb50aeb0003998b24d7df9063a6a41cb1..53f0735817bb7cf984f3fada24c7e8960941860c 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 93b80e5da8d4d5888982b30ffed5d5dc5f2cc470..1f3770a8a7286fd7650f76d46917408d0ff52b96 100644 (file)
@@ -120,7 +120,7 @@ static void omap3_save_secure_ram_context(void)
                 * will hang the system.
                 */
                pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON);
-               ret = _omap_save_secure_sram((u32 *)
+               ret = _omap_save_secure_sram((u32 *)(unsigned long)
                                __pa(omap3_secure_ram_storage));
                pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state);
                /* Following is for error tracking, it should not happen */
index a085d9cc1f5d0c01fbe02e2f490e18037e450a33..8d95aa543ef562f65fe18e6ed0fd07ed23dbc505 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) || defined(CONFIG_SOC_AM43XX)
 void omap44xx_prm_reconfigure_io_chain(void);
 #else
 static inline void omap44xx_prm_reconfigure_io_chain(void)
index f9423493ed36407627f2bf1e7bdab115d58ad2c1..584439bfa59f4ea0631deecac94920eb4eb97280 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 2d4a7b4d5d78b8c5d69dd985f6fbef683f8699e3..3aa264640c9dd5a0457b99e5a34897bd684ad3ba 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 fe2eb8394dffe9c289a0a3a2214efc4af24bdb0b..ab93441e596ed7eb6d3e0f4552d029626e81e42c 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 133109ec7332b23d1c412509f66a31f52aaaa412..a7c30eb0c8db18aaffd429ec7e637a14a892ad81 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 1255ee00f3d1d7c1f80f9cd0b2a28936c47353ad..9f6ec167902a6bc1c2db823d257e006550a3c3b0 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 f44532fc648b1982ded0d249a18005b84647767d..fab30d666cc72534493839521324a577a40e5a54 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 dd70343c87085edf4da384fedd434a339802756c..08ccc0718f319e4f7209e8592e50d0f26b92862d 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 acc9d3cc0762174f071a22a93cf8ae34f49b9cbb..f70583fee59f8b61e59a1022f07b36c26fd3210c 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 17d4c53017cade633c95315782128c0e0620c776..e54a296fb81f8b045f814860c0dc729edb71d2c4 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 100b176f7e882aaa2ba5047fefc19eb44d514394..7691c974ca4bd0b6ade4c467e3ea6a26c611d9ce 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 0742721ced2da7eebf983026f288265e039e139f..956fd24ee6fdca69b63bed29ebe4b9ebe567fd83 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 3133ba82c50807ec105a57f1dfe4b7d06a887c31..9a4e470f162bc0fc2403a1985e25d0cfd5bd2018 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 969b0ba7fa703a688a7078250c2118a3e19fe4b3..8386dc30b3e40d7c8eefac202411159932553327 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 62aea3e835f315266ba887367310ec249503cd21..01de542432a6107b896277da2c5c392c65e9aff6 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 4680efe553459bd6c98e3d4c771d7633c3a840a1..a71da84e784b75fcf9f740758ee0cb0604d3f741 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 9c363c081d3facb3be9ebe8693b32c2db9702bbc..29905b127ad988b68ec02a09b441cb48e9c59704 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 2513d8f4931f49675dfd8322edd03c2ffbd3ca0f..e1a121b36cfa6eef0e9dec7155da7f929e1075ed 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 36cf7cf95ec1d5d4fe2fd764067b7d640247aa2e..77daea478e88709aa20568d30073ae3c625efa73 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 74dd47988b415b217dbcd931794e4fd40d5dcdd4..952b6a040d1fcaa5694d21459acec9880ba626f0 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 a83db46320bc6d1e8345ebee8aefd3845f9b347f..4a18d49a63e0b5d7e84dc0018f9473d8f12f164a 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 206b1f7546d16e973aaa60030ce777684cdbfe57..034b7fe45c49294908bfc5b3716d52f6d9bec3e6 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 aca7d16e195dec0fda2989680cc58e73abb50315..758e31b265501de472aa6eba453038385e102536 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 e8064044ef796d35a12bfcc0e06c86dea065dca1..614a03a92cf70545be3fd60bb66a29dc46b1fb6e 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 0f47237be3b2dd2286fe0582791548d16df3a786..a6b338fd0470d6b55f0601058f4dc5446cfc480c 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 2a7b32ca5c96a3037c007eeb032092cf2127bfec..d5ea938cc9a1a03f7b6f884155de620a3fc0c3b3 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 0b00304c1e91840f1760c4cae2c80b4d9441ea79..9efdcc03df3b5477bc51ccb8196f0f7fcccabd97 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 5949296e88fdc07af89538478f88e0a142e5f3b7..c3cacc067efed25aff9740a2d65b795c4dd59266 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 7c57a221785e65d3aeb55e9c637397b91a857daa..9e256b9fc9303a9bf3b5411e0b8d70287254e036 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 6d72bb992e389b59872b3fead4742d4d3669e3bb..f52cc15c2d85a221a161f5c5d18fbf30c815dd72 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 8bc8e4c5884767f381c09da79ffcf2b52e2ffdc9..958e3cbf0ac2f2110e7f4fc9ca41dc19e66e4ca8 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 835833e3c4f8fb193f6d177c88ef35199488b33f..d71654bc8d54d0be5aef108a43d5f0673bd68d40 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 d7aa52ea6cfcb7fa92f98a703b5b17914585fa36..bc471973cf0424f3a040351d8ef2def5c458894d 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 d4639c5066222ea785f3dab068f46874fd52513c..9a4e910c3796154c8fa6c167851a8f6b112265f3 100644 (file)
@@ -209,13 +209,3 @@ void __init tegra_init_fuse(void)
                tegra_sku_id, tegra_cpu_process_id,
                tegra_core_process_id);
 }
-
-unsigned long long tegra_chip_uid(void)
-{
-       unsigned long long lo, hi;
-
-       lo = tegra_fuse_readl(FUSE_UID_LOW);
-       hi = tegra_fuse_readl(FUSE_UID_HIGH);
-       return (hi << 32ull) | lo;
-}
-EXPORT_SYMBOL(tegra_chip_uid);
index ce553d557c31caf1670e50ceadad19c067d2864c..73368176c6e8592ecc5f2f0155d5598885692fd3 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 033d34dcbd3fb8a8e1325900ddecdeb64090e874..c26ef5b92ca78587ce35b0f597a9cea66f9d592a 100644 (file)
 #define A15_BX_ADDR0           0x68
 #define A7_BX_ADDR0            0x78
 
+/* SPC CPU/cluster reset statue */
+#define STANDBYWFI_STAT                0x3c
+#define STANDBYWFI_STAT_A15_CPU_MASK(cpu)      (1 << (cpu))
+#define STANDBYWFI_STAT_A7_CPU_MASK(cpu)       (1 << (3 + (cpu)))
+
 /* SPC system config interface registers */
 #define SYSCFG_WDATA           0x70
 #define SYSCFG_RDATA           0x74
@@ -213,6 +218,41 @@ void ve_spc_powerdown(u32 cluster, bool enable)
        writel_relaxed(enable, info->baseaddr + pwdrn_reg);
 }
 
+static u32 standbywfi_cpu_mask(u32 cpu, u32 cluster)
+{
+       return cluster_is_a15(cluster) ?
+                 STANDBYWFI_STAT_A15_CPU_MASK(cpu)
+               : STANDBYWFI_STAT_A7_CPU_MASK(cpu);
+}
+
+/**
+ * ve_spc_cpu_in_wfi(u32 cpu, u32 cluster)
+ *
+ * @cpu: mpidr[7:0] bitfield describing CPU affinity level within cluster
+ * @cluster: mpidr[15:8] bitfield describing cluster affinity level
+ *
+ * @return: non-zero if and only if the specified CPU is in WFI
+ *
+ * Take care when interpreting the result of this function: a CPU might
+ * be in WFI temporarily due to idle, and is not necessarily safely
+ * parked.
+ */
+int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster)
+{
+       int ret;
+       u32 mask = standbywfi_cpu_mask(cpu, cluster);
+
+       if (cluster >= MAX_CLUSTERS)
+               return 1;
+
+       ret = readl_relaxed(info->baseaddr + STANDBYWFI_STAT);
+
+       pr_debug("%s: PCFGREG[0x%X] = 0x%08X, mask = 0x%X\n",
+                __func__, STANDBYWFI_STAT, ret, mask);
+
+       return ret & mask;
+}
+
 static int ve_spc_get_performance(int cluster, u32 *freq)
 {
        struct ve_spc_opp *opps = info->opps[cluster];
index dbd44c3720f98e711e5cabf28e6734847d8c7ea8..793d065243b9e469300be86a3325f777d7a37b4c 100644 (file)
@@ -20,5 +20,6 @@ void ve_spc_global_wakeup_irq(bool set);
 void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set);
 void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr);
 void ve_spc_powerdown(u32 cluster, bool enable);
+int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster);
 
 #endif
index 05a364c5077a7a40f4c3a348bced3a70e22ea280..29e7785a54bcbbb3e4e7fa3f2f46180e430ad91c 100644 (file)
@@ -12,6 +12,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include "spc.h"
 
 /* SCC conf registers */
+#define RESET_CTRL             0x018
+#define RESET_A15_NCORERESET(cpu)      (1 << (2 + (cpu)))
+#define RESET_A7_NCORERESET(cpu)       (1 << (16 + (cpu)))
+
 #define A15_CONF               0x400
 #define A7_CONF                        0x500
 #define SYS_INFO               0x700
 #define SPC_BASE               0xb00
 
+static void __iomem *scc;
+
 /*
  * We can't use regular spinlocks. In the switcher case, it is possible
  * for an outbound CPU to call power_down() after its inbound counterpart
@@ -190,6 +197,55 @@ static void tc2_pm_power_down(void)
        tc2_pm_down(0);
 }
 
+static int tc2_core_in_reset(unsigned int cpu, unsigned int cluster)
+{
+       u32 mask = cluster ?
+                 RESET_A7_NCORERESET(cpu)
+               : RESET_A15_NCORERESET(cpu);
+
+       return !(readl_relaxed(scc + RESET_CTRL) & mask);
+}
+
+#define POLL_MSEC 10
+#define TIMEOUT_MSEC 1000
+
+static int tc2_pm_power_down_finish(unsigned int cpu, unsigned int cluster)
+{
+       unsigned tries;
+
+       pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+       BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
+
+       for (tries = 0; tries < TIMEOUT_MSEC / POLL_MSEC; ++tries) {
+               /*
+                * Only examine the hardware state if the target CPU has
+                * caught up at least as far as tc2_pm_down():
+                */
+               if (ACCESS_ONCE(tc2_pm_use_count[cpu][cluster]) == 0) {
+                       pr_debug("%s(cpu=%u, cluster=%u): RESET_CTRL = 0x%08X\n",
+                                __func__, cpu, cluster,
+                                readl_relaxed(scc + RESET_CTRL));
+
+                       /*
+                        * We need the CPU to reach WFI, but the power
+                        * controller may put the cluster in reset and
+                        * power it off as soon as that happens, before
+                        * we have a chance to see STANDBYWFI.
+                        *
+                        * So we need to check for both conditions:
+                        */
+                       if (tc2_core_in_reset(cpu, cluster) ||
+                           ve_spc_cpu_in_wfi(cpu, cluster))
+                               return 0; /* success: the CPU is halted */
+               }
+
+               /* Otherwise, wait and retry: */
+               msleep(POLL_MSEC);
+       }
+
+       return -ETIMEDOUT; /* timeout */
+}
+
 static void tc2_pm_suspend(u64 residency)
 {
        unsigned int mpidr, cpu, cluster;
@@ -232,10 +288,11 @@ static void tc2_pm_powered_up(void)
 }
 
 static const struct mcpm_platform_ops tc2_pm_power_ops = {
-       .power_up       = tc2_pm_power_up,
-       .power_down     = tc2_pm_power_down,
-       .suspend        = tc2_pm_suspend,
-       .powered_up     = tc2_pm_powered_up,
+       .power_up               = tc2_pm_power_up,
+       .power_down             = tc2_pm_power_down,
+       .power_down_finish      = tc2_pm_power_down_finish,
+       .suspend                = tc2_pm_suspend,
+       .powered_up             = tc2_pm_powered_up,
 };
 
 static bool __init tc2_pm_usage_count_init(void)
@@ -269,7 +326,6 @@ static void __naked tc2_pm_power_up_setup(unsigned int affinity_level)
 static int __init tc2_pm_init(void)
 {
        int ret, irq;
-       void __iomem *scc;
        u32 a15_cluster_id, a7_cluster_id, sys_info;
        struct device_node *np;
 
index 2a5907b5c8d2272bcc366d01f6da6e5312d7d718..ff379ac115df0f45311ce9c7db0f42a5fde130ea 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 78eeeca78f5ab331707fcd73b4956c503c2d880b..580ef2de82d728f8ecfde5f5f3b208a2e5525b06 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 5c668b7a31f97e6df35dcec53b79d5a70a9d1d0b..55764a7ef1f021934ba2f0b0136fc1b1df2a2799 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 60920f62fdf5994f04477bc94aba09dcd349385a..bd1781979a391825043d078192666e5a846cdae5 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 d51f9565567cd1d27a197ab83a7ab35618072147..be4ad0b21c082abf990d00bec2f14d665672e78d 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 43841033afd3262ecc8b8e6a129af425c74c230d..12969523414cf2d0b972bdfb8b3ff50b9c781c8f 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 bb0bf1bfc05dd6868bf58d7cf4fac276c143db43..88c8b6c1341a445bdc425955383a8563033edd90 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 84fcc5018284b6cee3dca436dd4ec7634e3b00cc..519c4b2c06873dc82f7ed2ebff9ccb698691e24f 100644 (file)
@@ -6,6 +6,8 @@
 
 /dts-v1/;
 
+/memreserve/ 0x80000000 0x00010000;
+
 / {
        model = "Foundation-v8A";
        compatible = "arm,foundation-aarch64", "arm,vexpress";
index 8d1810001aeffc0be57b9edc5919941f8a49e91a..fd0c0c0e447a657115c116f45bf9edd693b339a3 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 b56e5b5df881e514ffeb21b227ebbd8b54fe3d29..4cc813eddacbebee4c84f864f103bb6492d7c193 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 aa11943b850213c77a32f87c1ce22c8aac87bf83..b2fcfbc51ecc4b0eaef6e4b20efd0f7afd2a9efd 100644 (file)
@@ -56,6 +56,9 @@ static inline void arch_local_irq_disable(void)
 #define local_fiq_enable()     asm("msr        daifclr, #1" : : : "memory")
 #define local_fiq_disable()    asm("msr        daifset, #1" : : : "memory")
 
+#define local_async_enable()   asm("msr        daifclr, #4" : : : "memory")
+#define local_async_disable()  asm("msr        daifset, #4" : : : "memory")
+
 /*
  * Save the current interrupt enable state.
  */
index a5f28e2720c7e3a930463134367983d6df2c2724..c98ef4771c7389b989d073b7a933047908fcf0a2 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 eec073875218049371eb7beed7715f10ae3b067a..dd8ecfc3f9952fbf86e5d3dd69012ca9ab9a8385 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 0859a4ddd1e7d0e8b1792416b19a8f9908457af7..5d85a02d1231e9b6a3738e48cee574e19f987c6b 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 efe609c6a3c95385aa97c61895ab76af4c198450..680f74e674971ab5062047369d6994edddc64e8e 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 f214069ec5d5c594731399ca2db176ebe54c447e..9bea6e74a0018154727403db31662c43e6a2aa23 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 d57e66845c8610fdaf53754bee71f9de8cadf983..755f86143320167038e24e9be093e61b8b307e65 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 17bd3af0a1177d094f27a9a4294dd6eae5103098..7f2b60affbb49509f290a5a56e5842fd1f29f4eb 100644 (file)
  * Software defined PTE bits definition.
  */
 #define PTE_VALID              (_AT(pteval_t, 1) << 0)
-#define PTE_PROT_NONE          (_AT(pteval_t, 1) << 2) /* only when !PTE_VALID */
-#define PTE_FILE               (_AT(pteval_t, 1) << 3) /* only when !pte_present() */
+#define PTE_FILE               (_AT(pteval_t, 1) << 2) /* only when !pte_present() */
 #define PTE_DIRTY              (_AT(pteval_t, 1) << 55)
 #define PTE_SPECIAL            (_AT(pteval_t, 1) << 56)
+                               /* bit 57 for PMD_SECT_SPLITTING */
+#define PTE_PROT_NONE          (_AT(pteval_t, 1) << 58) /* only when !PTE_VALID */
 
 /*
  * VMALLOC and SPARSEMEM_VMEMMAP ranges.
@@ -254,7 +255,7 @@ static inline int has_transparent_hugepage(void)
 #define pgprot_noncached(prot) \
        __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE))
 #define pgprot_writecombine(prot) \
-       __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_GRE))
+       __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC))
 #define pgprot_dmacoherent(prot) \
        __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC))
 #define __HAVE_PHYS_MEM_ACCESS_PROT
@@ -357,18 +358,20 @@ extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
 
 /*
  * Encode and decode a swap entry:
- *     bits 0, 2:      present (must both be zero)
- *     bit  3:         PTE_FILE
- *     bits 4-8:       swap type
- *     bits 9-63:      swap offset
+ *     bits 0-1:       present (must be zero)
+ *     bit  2:         PTE_FILE
+ *     bits 3-8:       swap type
+ *     bits 9-57:      swap offset
  */
-#define __SWP_TYPE_SHIFT       4
+#define __SWP_TYPE_SHIFT       3
 #define __SWP_TYPE_BITS                6
+#define __SWP_OFFSET_BITS      49
 #define __SWP_TYPE_MASK                ((1 << __SWP_TYPE_BITS) - 1)
 #define __SWP_OFFSET_SHIFT     (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+#define __SWP_OFFSET_MASK      ((1UL << __SWP_OFFSET_BITS) - 1)
 
 #define __swp_type(x)          (((x).val >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK)
-#define __swp_offset(x)                ((x).val >> __SWP_OFFSET_SHIFT)
+#define __swp_offset(x)                (((x).val >> __SWP_OFFSET_SHIFT) & __SWP_OFFSET_MASK)
 #define __swp_entry(type,offset) ((swp_entry_t) { ((type) << __SWP_TYPE_SHIFT) | ((offset) << __SWP_OFFSET_SHIFT) })
 
 #define __pte_to_swp_entry(pte)        ((swp_entry_t) { pte_val(pte) })
@@ -382,15 +385,15 @@ extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
 
 /*
  * Encode and decode a file entry:
- *     bits 0, 2:      present (must both be zero)
- *     bit  3:         PTE_FILE
- *     bits 4-63:      file offset / PAGE_SIZE
+ *     bits 0-1:       present (must be zero)
+ *     bit  2:         PTE_FILE
+ *     bits 3-57:      file offset / PAGE_SIZE
  */
 #define pte_file(pte)          (pte_val(pte) & PTE_FILE)
-#define pte_to_pgoff(x)                (pte_val(x) >> 4)
-#define pgoff_to_pte(x)                __pte(((x) << 4) | PTE_FILE)
+#define pte_to_pgoff(x)                (pte_val(x) >> 3)
+#define pgoff_to_pte(x)                __pte(((x) << 3) | PTE_FILE)
 
-#define PTE_FILE_MAX_BITS      60
+#define PTE_FILE_MAX_BITS      55
 
 extern int kern_addr_valid(unsigned long addr);
 
index 23a3c4791d86cb71f9a2520e2176212b1e107503..720e70b66ffdcf6eff60efc964c32e9a0584325b 100644 (file)
@@ -88,12 +88,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
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 6a0a9b132d7af11714348f3950990a9854d56ba6..4ae68579031db9fb1e8e5a04689499fde2fcbc4c 100644 (file)
@@ -248,7 +248,8 @@ static int brk_handler(unsigned long addr, unsigned int esr,
 int aarch32_break_handler(struct pt_regs *regs)
 {
        siginfo_t info;
-       unsigned int instr;
+       u32 arm_instr;
+       u16 thumb_instr;
        bool bp = false;
        void __user *pc = (void __user *)instruction_pointer(regs);
 
@@ -257,18 +258,21 @@ int aarch32_break_handler(struct pt_regs *regs)
 
        if (compat_thumb_mode(regs)) {
                /* get 16-bit Thumb instruction */
-               get_user(instr, (u16 __user *)pc);
-               if (instr == AARCH32_BREAK_THUMB2_LO) {
+               get_user(thumb_instr, (u16 __user *)pc);
+               thumb_instr = le16_to_cpu(thumb_instr);
+               if (thumb_instr == AARCH32_BREAK_THUMB2_LO) {
                        /* get second half of 32-bit Thumb-2 instruction */
-                       get_user(instr, (u16 __user *)(pc + 2));
-                       bp = instr == AARCH32_BREAK_THUMB2_HI;
+                       get_user(thumb_instr, (u16 __user *)(pc + 2));
+                       thumb_instr = le16_to_cpu(thumb_instr);
+                       bp = thumb_instr == AARCH32_BREAK_THUMB2_HI;
                } else {
-                       bp = instr == AARCH32_BREAK_THUMB;
+                       bp = thumb_instr == AARCH32_BREAK_THUMB;
                }
        } else {
                /* 32-bit ARM instruction */
-               get_user(instr, (u32 __user *)pc);
-               bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM;
+               get_user(arm_instr, (u32 __user *)pc);
+               arm_instr = le32_to_cpu(arm_instr);
+               bp = (arm_instr & ~0xf0000000) == AARCH32_BREAK_ARM;
        }
 
        if (!bp)
index e1166145ca29b59801c84420e1f98225650f4580..4d2c6f3f0c4186da25fb1d6932d2671040941df2 100644 (file)
@@ -309,15 +309,12 @@ el1_irq:
 #ifdef CONFIG_TRACE_IRQFLAGS
        bl      trace_hardirqs_off
 #endif
-#ifdef CONFIG_PREEMPT
-       get_thread_info tsk
-       ldr     w24, [tsk, #TI_PREEMPT]         // get preempt count
-       add     w0, w24, #1                     // increment it
-       str     w0, [tsk, #TI_PREEMPT]
-#endif
+
        irq_handler
+
 #ifdef CONFIG_PREEMPT
-       str     w24, [tsk, #TI_PREEMPT]         // restore preempt count
+       get_thread_info tsk
+       ldr     w24, [tsk, #TI_PREEMPT]         // restore preempt count
        cbnz    w24, 1f                         // preempt count != 0
        ldr     x0, [tsk, #TI_FLAGS]            // get flags
        tbz     x0, #TIF_NEED_RESCHED, 1f       // needs rescheduling?
@@ -507,22 +504,10 @@ el0_irq_naked:
 #ifdef CONFIG_TRACE_IRQFLAGS
        bl      trace_hardirqs_off
 #endif
-       get_thread_info tsk
-#ifdef CONFIG_PREEMPT
-       ldr     w24, [tsk, #TI_PREEMPT]         // get preempt count
-       add     w23, w24, #1                    // increment it
-       str     w23, [tsk, #TI_PREEMPT]
-#endif
+
        irq_handler
-#ifdef CONFIG_PREEMPT
-       ldr     w0, [tsk, #TI_PREEMPT]
-       str     w24, [tsk, #TI_PREEMPT]
-       cmp     w0, w23
-       b.eq    1f
-       mov     x1, #0
-       str     x1, [x1]                        // BUG
-1:
-#endif
+       get_thread_info tsk
+
 #ifdef CONFIG_TRACE_IRQFLAGS
        bl      trace_hardirqs_on
 #endif
index fecdbf7de82e9a94d6f467f5999428cfcf623145..6777a2192b83846f1065f442f5777d4092e9bc0c 100644 (file)
@@ -636,28 +636,27 @@ static int compat_gpr_get(struct task_struct *target,
 
        for (i = 0; i < num_regs; ++i) {
                unsigned int idx = start + i;
-               void *reg;
+               compat_ulong_t reg;
 
                switch (idx) {
                case 15:
-                       reg = (void *)&task_pt_regs(target)->pc;
+                       reg = task_pt_regs(target)->pc;
                        break;
                case 16:
-                       reg = (void *)&task_pt_regs(target)->pstate;
+                       reg = task_pt_regs(target)->pstate;
                        break;
                case 17:
-                       reg = (void *)&task_pt_regs(target)->orig_x0;
+                       reg = task_pt_regs(target)->orig_x0;
                        break;
                default:
-                       reg = (void *)&task_pt_regs(target)->regs[idx];
+                       reg = task_pt_regs(target)->regs[idx];
                }
 
-               ret = copy_to_user(ubuf, reg, sizeof(compat_ulong_t));
-
+               ret = copy_to_user(ubuf, &reg, sizeof(reg));
                if (ret)
                        break;
-               else
-                       ubuf += sizeof(compat_ulong_t);
+
+               ubuf += sizeof(reg);
        }
 
        return ret;
@@ -685,28 +684,28 @@ static int compat_gpr_set(struct task_struct *target,
 
        for (i = 0; i < num_regs; ++i) {
                unsigned int idx = start + i;
-               void *reg;
+               compat_ulong_t reg;
+
+               ret = copy_from_user(&reg, ubuf, sizeof(reg));
+               if (ret)
+                       return ret;
+
+               ubuf += sizeof(reg);
 
                switch (idx) {
                case 15:
-                       reg = (void *)&newregs.pc;
+                       newregs.pc = reg;
                        break;
                case 16:
-                       reg = (void *)&newregs.pstate;
+                       newregs.pstate = reg;
                        break;
                case 17:
-                       reg = (void *)&newregs.orig_x0;
+                       newregs.orig_x0 = reg;
                        break;
                default:
-                       reg = (void *)&newregs.regs[idx];
+                       newregs.regs[idx] = reg;
                }
 
-               ret = copy_from_user(reg, ubuf, sizeof(compat_ulong_t));
-
-               if (ret)
-                       goto out;
-               else
-                       ubuf += sizeof(compat_ulong_t);
        }
 
        if (valid_user_regs(&newregs.user_regs))
@@ -714,7 +713,6 @@ static int compat_gpr_set(struct task_struct *target,
        else
                ret = -EINVAL;
 
-out:
        return ret;
 }
 
index 0bc5e4cbc017674db8785f434fa3cff0b30d4c56..bd9bbd0e44edf176c262cfad0e32b9f62599d4b7 100644 (file)
@@ -205,6 +205,11 @@ u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
 
 void __init setup_arch(char **cmdline_p)
 {
+       /*
+        * Unmask asynchronous aborts early to catch possible system errors.
+        */
+       local_async_enable();
+
        setup_processor();
 
        setup_machine_fdt(__fdt_pointer);
index a5aeefab03c3e9cd2c0a241d4f4632bb0933b31f..a0c2ca602cf85bebc6e7996454adb9e47fe9456d 100644 (file)
@@ -160,6 +160,7 @@ asmlinkage void secondary_start_kernel(void)
 
        local_irq_enable();
        local_fiq_enable();
+       local_async_enable();
 
        /*
         * OK, it's off to the idle thread for us
index 21e90820bd23cddabf676e920890cc23d8fa3437..4480ab339a007549c87f3bacc27c7c54f8b50b22 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 2c3ff67a8ecbef4625769f53811bfb9958083d1a..3f0731e53274c92b540a2eab2d0f154005d815f4 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 9beaca0334375a0be2e189fb8714c38b56ea2203..8da56067c304cd6b15e654015f8ecb6528c9bb65 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 be240404ba965b91c32c8e15976648b2e331514c..74a8d87e542b726b2022a21d2851ce63f124d6d7 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 4488fa27fe948c2e73018e962628c7883327bf07..2ffc298f061b3e7704e89dd634ece905ff13dcf4 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 996cb656474e267920ad6856f6445156349b0579..45f563ed73fd51e6d4b0590a56d0f7fa3e5f4c48 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 bc7e8ae479ee4f7f9be717cbcd38d0731773a6f9..1aba19d68c5ededf4ce986340bf410bb8e05eb3e 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 6dc62e1f94c7801532c9a3bc0be13abe3f8009e4..a978f3fe7c25a2fc651b19179b846f3939047bd9 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 3b85eaddf525f2be65d5772be3f28051afac7b43..08d8a3d76ea8628b6d749370c4edec3efb5b578c 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 d5dd435bf8f4769cc5d20184eca9db07c81f64d1..4f02da3ffefa63d61bcaa8de9a6429a819c800c2 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 50abc21619a8a288b9c46a2ac0d38ca56ce344e7..71242f0d39c6d7ef2caed95524356e159cd5796c 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 4faf1ce600616d82108523b157d065baa6cdeee1..573a9584dd57eafeefdb18754d0e0df7a9b65f77 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 ac18bc4da7f7acfe154ca532093ea5712fe714ad..9eae6effad14029c1f128277eb8c0eec89fe2be5 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 9ba9e749b3f34d7c2760d1d9784ff9ede9528ef3..5b813a8abf0946645d00979dc1bcd997f43362bd 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 e472216e0c9717a2bd577775f9c686251c9688b9..6c6f7cf1e75ac99ce9f849d6a2343904cc2996d5 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 e58aa9356fafc080e311ac9a080ac05af49fb9e6..a654df7dba462c68de298a3d7b3741b0a5855cf5 100644 (file)
@@ -13,5 +13,4 @@
 
 #define COMMAND_LINE_SIZE 256
 
-
 #endif /* _UAPI__ASM_AVR32_SETUP_H__ */
index c62fba41739aff13c647044b8c1017a7c6307614..b94cf8b60b73df0567125f2877aed7dbd7308be9 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 e04062b5f39fe421045ae6b6a4f5bd13920ca38d..27e56bf6377f6c00647b0ae5a0ff4c2cfce20542 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 1b77a93eff500c14ac9594c9f6098007353c8a78..ffe8c770cafd86efe310fe9882210691fcf44c44 100644 (file)
@@ -118,5 +118,4 @@ typedef struct sigaltstack {
        size_t ss_size;
 } stack_t;
 
-
 #endif /* _UAPI__ASM_AVR32_SIGNAL_H */
index 4399364214349674999c872ca4779c7089c22160..cbf902e4cd9e9ef1528e48a24f3d56c3fc2d889d 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 0802d742f97d79138bbc5b4400f2fe1c145dece5..d04785453532a51fcfa0d3b654e875e18f1bfb30 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 e72881e10230506e5adba38057738680234892e1..c06acef7fce7f42d6204d5d97a1c7a42100409a6 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 14cc737bbca6e909dbd7ec44d36703731f8ca519..1a03549e7dc5ea9ef3818194cd0a5a319d90eddc 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 366adc5ebb100db9924f584568327db7ab70672c..32789ccb38f8434cfd494dbaed6088eb304156de 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 b8ef8ea6335284bac7281f518410b2c568c9a632..c8a0081556c4da3b8dfb4041392fb1bb95591f40 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 bb34ad349dfc1a12d35d1e48066baa71f4c4bee2..7c986c4e99b55cf3727ceb68eeb78841c8d99b11 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 3eaa68753adba04cd4382424b4b8eeef9a1b440d..8822bf46ddc683758ea9c6be98d0f15d995c9394 100644 (file)
 #define __NR_eventfd           281
 #define __NR_setns             283
 
-
 #endif /* _UAPI__ASM_AVR32_UNISTD_H */
index 9899d3cc6f03b109f352faa9ac51387f96497cf7..7301f4806bbede59cf5eaa48649802ec75cddacf 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 6163bd0acb958ae3a016ec125a574764fec48c12..59eae6dfbed2b5f1167864bf6311b0b231da9a10 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 e887b57c3176705d90beeebcc39123836f5eff1b..9ceccef9c6490ff229fc1851a6f467be7e5e4232 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 13eb73231a9a417a243920c9b641db735b185559..4ca39ab6b2bf98c12b260fef822473ae75820c2f 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 c078dd78d998b54a5e39889bfa545dc58b964030..58b54a6d5a1627285ccfcfe76dae5eb0b5fec7ff 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 4ae1144a457821c9349cfa0c4026e481f86a6545..2fd04f10cc266b86d6a88c786278c18fd2a604fb 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 4fbf83575db1ef8c363d75b374fe25cec15e162d..4b2a992794d77b187c657ad515d9db17bf4002ed 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 3894005337ba7d8628de4f7de2f9f7dd8f5b603d..55f473bdad361a42ca625433a8d8bfb629f378bf 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 ed978f1c5cb9970933c102300722ae8051774d7e..a017359c182668f71583ce013ae92263b95c69e6 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 94acb586832e69a23036e6967ceb0a3142876d66..334ec7b12188d78932c57ef08e360e6e19d542dd 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 372eb54944eff3cbb12269c7a69947f9b1c86ee4..d495000b81a05c1e58aa824391d0d4c8ea857d87 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 be9edb28f96bd6bea7fb5b3604d572f6c35b1fc8..006da1edcf84bf42d95c6a7476db48f04353abca 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 10dc142c518d538db8c7154eb47ad36958ea63e8..cf7cb725cfa2bff44dd645333ff982ddf6e8632e 100644 (file)
 #include <linux/types.h>
 
 /*
- * bfin pint registers layout
+ * gpio pint registers layout
  */
 struct bfin_pint_regs {
        u32 mask_set;
index 2bcbf94b1edf4cbe9108b108f85bd253bb201d11..b0fca44110b032dc59dc7b5d9485ba2724bf573c 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 d56a55ad83a7c2340a43744dcaeca8522909cb01..82beedd953f648849852acf15f50e789f6c04684 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 c32c8cc8db2e79e0ae4aa2d1c892c8ec9cff4c3a..07182513e794cb2bd89bc9645c25b2bd75611c40 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 fa0843d5d77ac382be120c3aa4d426adbc6fe0de..d1cb6a86f80a9fe8fc70c45214fd55e0af9cb4d3 100644 (file)
 extern u8 sec_int_priority[];
 
 /*
- * bfin pint registers layout
+ * gpio pint registers layout
  */
 struct bfin_pint_regs {
        u32 mask_set;
index fe34191eef0bb00cfac8c9c9338144eb5bdbc804..c48bb71a55ce7c56dcc412d125e4d380f9ab1dcc 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 d143fd8d2bc5075544e0bc6655306bd66bae90ca..ca75613231c84474ad276c667eb4cd415027532e 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 87bfe549ad3f5044b8ce652434b98d00470672fc..1387a94bcfd5a30e68fb00d9e293eea7db710d98 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 82f301c117a508b562cf16bd698f61e1ed9f0797..2bbae07838198912aa4beda231a0f96eaa647bd5 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 957dd00ea561ce3881a0af87394d349fab04f449..77ea09b8bce1bb775c725f71211de3d3c9224229 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 4c8dc562bd90dce27d0236140434a34120fe33d7..d4e9ef87076dbe4e0013876c27fdd84d69840d75 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 17bb12d760b2eb2858cb39bb8e057fd51c5c96e5..04126f7bfab2a918e4e8c509d9fd989a2e7217b5 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 6da975db112fdabff1125ed0f3ecb38df892caae..235ece437dddc7196be819a8b7b30dc3f26028a5 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 07c8c40c52b312ddfee756ca8ea5b0619ddb4a65..55dede18c032e37195cdb5c17ccebfddebc505a8 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 bebd7eadc7720c3e3ddac4fe455a8bef37308042..af29e17c01814a2d88be2ca5d535f31441ddc474 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 f6084bc524e85e578487d34dea06f0e23b4a8214..41907d25ed384e2ee42e46d1df461bbd2886732f 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 99041b07e61094d964fd1289481c11f739407d04..09df2608f40a9c3dc73220abf96e177b5ade447f 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 679bf6d664871377ef33a7c16f5cb6b25169c253..4c9d382d7798e6640f52034f5e713a025a7bdab8 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 f7c32406a711a5b619d53037fe76d8689107e3c6..a59dad3b3695112e6f15633de0f3b9304af17532 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 7740ab10a17192cb0596d0ee442282d549b3ba33..4e4119b0e6915fc7e05c1536d9248d638cb4fe16 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 d43daf192b21d54df034e52c809a4ac4b2178161..4c530a82fc469f976b8a38a341f077a43f9c9998 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 989dd3fe8de19d9fc40de248f5788f359eb3ebc6..db95f570705fd4dd61443b1a06fbc1fa9a266763 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 80775f55f03f9293c2d05d2598e349f08ef9b56e..71fbaaa495ccc18af5f33cdaef92c0ab228dad8e 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 96a8d927db2851c9c17d8b48f6b47da66f9dd572..5767cdfc08db8b671e2536bb093fbe4a921e47c7 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 cade13dd0299f8754d18d87eded8661b64bf3eb8..5957cf61f8980641c54271ab4030b1d765570001 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 7a53530f22c219eb877d81d2392a85722e9503dc..ddea607f948aaa61932b022a14a390df9e816f8c 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 f8280a766a78672238d34ab41524f21f286b5e14..074fde49c9e6234f22e3e5b132bcdcfe176e9757 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 5a9ff1c3c3e912c5d0435ea86900a664e525fa16..cb592773c78b1ef1f86faa4a4190c507c4e9fdbf 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 bdfd8789b37661da691bdf320841a21043cd4cd4..985bf80c622e6dea757f16e75cb5004210196cd3 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 2326790b7d8be4f9e6cffbea4c4a22fc1ab91f3e..9e4938d8ca4d297e331131a79d8458d97a27d256 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 b1725398b5af49683622765652104a85c3d95228..0640739cc20cf2895fca507babfe7de9096d8a41 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 75661fbf4529e6c903fcb2bb61e3b48e5656fd04..09ef94a8a7c3a18632f7d7bfb3f6edb291100c34 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 4c31c0ae215e87928098a9b60e2d978ae710f800..5f2ac4f64ddfd125e197ee045bba122cde8fa900 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 a979a41981689a7321a7e758a3c5ec744d6f9202..9fc78fc4444524681a6688d2273edae2581b4584 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 0fc7361989797d9a8c6fb6cc2a823367f75236e0..2d55a064ccac5ac335e339ce649cf210804842fd 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 c074f4c2e858e6cc76b16476a9fc24aea2566793..00171703402f19b576612f699bf733fe12252390 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 0c01543f10cd9c1ad53c9453294d185fc18730cd..7c3db9940ce1f1e1254561ff0992c3980127beeb 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 311a300d48cca82b82a5faa0bf6abbaee42cacef..75f25a8e300170ce85130da8fd2f739faa983059 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 db30ed276878dcbf793e667d6edb279c5234a60a..6c618529d9b96e3815568a486a146c31b4b6a005 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 313f3dd23cdc955f56af3fb581aa0e398d3a0f21..f9924fbcfe42b8f279d83758e4fc987cb32da409 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 2f02f264e6944dd7fd11d8d4ffe0e10244fe1f60..24bcba496c7505e94d5c45512c399e773106b029 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 48d80d5a666f80b64d5b62e318197b1752e29733..f868506e3350b0075799a30cc0e42206e7e21212 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 126131f94a2ca951a2db9c9a414e3c30a142baeb..21a4784ca5a186256cfbb5bc6d1d1b7cd4de3e1c 100644 (file)
@@ -35,8 +35,6 @@ struct thread_info {
 };
 #endif /* __ASSEMBLY__ */
 
-#define PREEMPT_ACTIVE         0x4000000
-
 #define INIT_THREAD_INFO(tsk)                  \
 {                                              \
        .task           = &tsk,                 \
index a78f5649e8decf3fbd80c3d1a54567644f6fd385..b54ac7aba850eaf02ac995a857e673102039f3be 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 4d7da384eea03d5a1101859a5814c7dccce53a9e..077d3a70fed1995611f89e0f5795c40da07378df 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 7f91c2fde5096b9eaaef2e33334cf0339b896352..23ac054c6e1ad929fa40e8aa033e51251c96538d 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 904fd9a4af4e45d472a121fdc678a523655059cc..447c33ef37fda3456b5fad22d705f123c91b4f84 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 36368eb07e13fe629d00ac1f1fdcc8ff06220ba2..e56abd2c1b4ffbac8f6fbd972ec029c3c69169fe 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 275d9285141cd733311628aceed30752c70e82f4..3104df0a4822fdf766aa8821e3dc17165e5bbc0c 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 7c4a3300614230ce8d956257412e8e0ab0ade8fb..b19e9c588a16733e81f5495593c881d2d9c7c170 100644 (file)
@@ -46,8 +46,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #ifdef CONFIG_4KSTACKS
 #define THREAD_SHIFT           12
 #else
index 655e1cadf6924ea54ab19b1274ede3d2a7b5e46b..e23cccde9c2759b9ea798a3a0705f7f242acd474 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 ebd35792482c104678dabdab9324002dc7ae2a31..7fdf7fabc7d7f0b752f6c8a4727b23c24a9f1438 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 de26ea6373ded64af3d7793489be9343a1ee1075..8c9d36591a031d6a84e81ae7485185aea125a5e3 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 17cc7ff8458ce2d16f9f8a3be5a4bdfee752030c..650de3976e7a5a337ab60dcc6ee5f0dd04fe22c0 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 9a357fffcfbe0a0c6767e5b12d70d22f23e5b1b7..820b7a313d9bfd593cdaa63e6374f0cf4bd15d6a 100644 (file)
@@ -92,7 +92,6 @@ void __init plat_mem_setup(void)
        _machine_restart = ar7_machine_restart;
        _machine_halt = ar7_machine_halt;
        pm_power_off = ar7_machine_power_off;
-       panic_timeout = 3;
 
        io_base = (unsigned long)ioremap(AR7_REGS_BASE, 0x10000);
        if (!io_base)
index e2b4ad55462f3477f6c4b5e63d54057064b92fde..28e49f226dc0286aa73b22c1d7c76b663af1729c 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 d71005835c007aeb8df0f8b288fbfb1f3045457d..9100122e5cef891c18767fee89c98858df661f11 100644 (file)
@@ -111,9 +111,6 @@ void __init plat_mem_setup(void)
        iomem_resource.start = EMMA2RH_IO_BASE;
        iomem_resource.end = EMMA2RH_ROM_BASE - 1;
 
-       /* Reboot on panic */
-       panic_timeout = 180;
-
        markeins_sio_setup();
 }
 
index 4d6fa0bf1305d7376c7ae2ce2201fc412a8791de..32966969f2f975f56bddbf9ea0aa514d8f435d97 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 a76fe5a57a9f64e302af248194ea9288cb4590b4..df69bfd2b006bc01f300094a8ae1a571058ef101 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 881d18b4e298009661619a1f98777fbc45b38ff2..b336037e8768cfd50e45ff877f759a0039bbeb5e 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 f9b24bfbdbae96f8cc31d58d7cb0318130ceab9f..4f58ef6d0eed5c58c9e58595fac36b4b7f747a2e 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 a7b044536de48e0f804d2270fc69a3cdbe6a3335..73b34827826c206a0e98c43be862a272395818b7 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 6d981bb337ecd8abf96324d1e0f2a006caa942de..54e75c77184b883fdbec601e1b2e97e49b1aa8c9 100644 (file)
@@ -92,7 +92,6 @@ static void __init xlp_init_mem_from_bars(void)
 
 void __init plat_mem_setup(void)
 {
-       panic_timeout   = 5;
        _machine_restart = (void (*)(char *))nlm_linux_exit;
        _machine_halt   = nlm_linux_exit;
        pm_power_off    = nlm_linux_exit;
index 214d123b79faf7f6659ae4127ffe938257fb26ae..921be5f77797706279d61c1c79f897a206546d07 100644 (file)
@@ -92,7 +92,6 @@ static void nlm_linux_exit(void)
 
 void __init plat_mem_setup(void)
 {
-       panic_timeout   = 5;
        _machine_restart = (void (*)(char *))nlm_linux_exit;
        _machine_halt   = nlm_linux_exit;
        pm_power_off    = nlm_linux_exit;
index 41707a245dea61c1cabd467f2bc3d2ec2ca28cd8..3462c831d0ea5f1b307fcd002645d7b328bc816f 100644 (file)
@@ -134,8 +134,6 @@ void __init plat_mem_setup(void)
 #error invalid SiByte board configuration
 #endif
 
-       panic_timeout = 5;  /* For debug.  */
-
        board_be_handler = swarm_be_handler;
 
        if (xicor_probe())
index 6aaa1607001a42980c95a1984c7775bc5359826a..8bde9237d13bf3dca2ab111b35ba921f496baec3 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 c67c2b5365a6dbef415d767a69db49f73ea2ad69..75dbe696f830cc93284a47f61fb5ae4a00f2a292 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 146bacf193eac3f6c27536bd4b4b04b73942b60b..0f25d5fa86f3ea5304112e852337e4ba7b06713e 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 224b4262486dbca41453861f95461b635bb40bcc..bf280eaccd36fd7e7b963176184450fb98126340 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 bd9ada693f9561ba15b581855d1b80c95f57db4b..e77a7c728081125233ebf569dac465960a997aff 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 4739b8302a58d38e478c7f58a10e7c22c0c20897..89076a66eee266d79782949f93a9cbba30371b1a 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 ea172bdfa36ae41b139ebccd5f72a357ebd4ec28..42fe5303a37008bd33777b2c539025f259c5c9fd 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 78405625e799016b46647db52711c77f5b7ef224..da1951a22907772ec776dde7761c765479b6436a 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 05c39ecd2efdcb51e1c5288501b5098217630a23..21484e5b9e9af1cec95e2d786d87816caccb13e4 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 10ff50f0202ac37181fe160fb7ac62f2d1a95dc9..ef872ae4c878970733342be87177b473bde09456 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 09a769b695720909a52b409f9a49fe40b99f4fa0..4fc7ccc0a2cf5b43549f9de2ed09124a196b5766 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 ee842a2d3f362562e5ad753007f11d1358ed13bb..70b9ce41835cdf2a3e345a39dc9e50197f6cb330 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 7dcde539d61e08b0bc586aa220b19ed31654c89c..b5f1858baf3399b1dcb10ef3f5d19debf50876d3 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 fc987a1c12a88828d66c9c322da3d0a514fd5143..f213f5b4c4239b961e260c659447886a8646f0bd 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 bc7cf120106b30e477264850e373f346eeb9bf82..d5f97ea3a4e1ab13a018de4d5d549f90efe4f299 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 63f4dd0b49c29c758807b68bbf9b7bf9b71911c3..4006964d8e12646761d954b9f73ff0b503e736b6 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 7c614d01f1fa42df36a23b3633f6d6ae7d47aa6a..f33113a6141e7540da2195cc72469152edfbecf2 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 b5507ec06b846f09ed4d38c5841b4eecaffb156e..413dc1769299685f00289193ca301ba7e2c0839d 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 7584a5df0fa4a76f5e2815fac25a38436a587955..9d08c71a967ed2e1e86189272369f4b794b8453c 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 2f898d63eb96ad4fb5b0cf0528aa69dfb54633cc..b2be8e8cb5c71471864e9fafebf6a475cab67bec 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
@@ -147,6 +147,10 @@ config EARLY_PRINTK
        bool
        default y
 
+config PANIC_TIMEOUT
+       int
+       default 180
+
 config COMPAT
        bool
        default y if PPC64
index 607acf54a425b2b50913ea6b4f48024a5d21aadc..0f4344e6fbca99f621daf6776811d1538e1e69af 100644 (file)
@@ -75,8 +75,10 @@ LDEMULATION  := lppc
 GNUTARGET      := powerpcle
 MULTIPLEWORD   := -mno-multiple
 else
+ifeq ($(call cc-option-yn,-mbig-endian),y)
 override CC    += -mbig-endian
 override AS    += -mbig-endian
+endif
 override LD    += -EB
 LDEMULATION    := ppc
 GNUTARGET      := powerpc
@@ -111,6 +113,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)
@@ -127,7 +130,12 @@ CFLAGS-$(CONFIG_POWER5_CPU) += $(call cc-option,-mcpu=power5)
 CFLAGS-$(CONFIG_POWER6_CPU) += $(call cc-option,-mcpu=power6)
 CFLAGS-$(CONFIG_POWER7_CPU) += $(call cc-option,-mcpu=power7)
 
+# Altivec option not allowed with e500mc64 in GCC.
+ifeq ($(CONFIG_ALTIVEC),y)
+E5500_CPU := -mcpu=powerpc64
+else
 E5500_CPU := $(call cc-option,-mcpu=e500mc64,-mcpu=powerpc64)
+endif
 CFLAGS-$(CONFIG_E5500_CPU) += $(E5500_CPU)
 CFLAGS-$(CONFIG_E6500_CPU) += $(call cc-option,-mcpu=e6500,$(E5500_CPU))
 
index 4c617bf8cdb24af8f3a202d40c4cc98b776768fd..4f6e48277c46170bb806bbb17e41c2887de4054f 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 510afa362de141465b50aada0cf0e7c9ceb83536..4143a9733cd01e0ad62d9d3d98902e7f414dd8f3 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 {
index cc00f4ddd9a7c1e1d5d3d8b2f869050b85204fa6..c409cbafb12678b0782edce46af71f5040f933f5 100644 (file)
                tlu@2f000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x2f000 0x1000>;
-                       interupts = <61 2 >;
+                       interrupts = <61 2>;
                        interrupt-parent = <&mpic>;
                };
 
                tlu@15000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x15000 0x1000>;
-                       interupts = <75 2>;
+                       interrupts = <75 2>;
                        interrupt-parent = <&mpic>;
                };
        };
index 53c1c6a9752f953a543b3d44ac134f0dfe321cc2..04cb410da48b78337b04fcfdc6b8940471917bda 100644 (file)
                tlu@2f000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x2f000 0x1000>;
-                       interupts = <61 2 >;
+                       interrupts = <61 2>;
                        interrupt-parent = <&mpic>;
                };
 
                tlu@15000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x15000 0x1000>;
-                       interupts = <75 2>;
+                       interrupts = <75 2>;
                        interrupt-parent = <&mpic>;
                };
        };
index 2152259831509ea89b1e64e2676c6e580de59c0f..73f8620f1ce7eeb714d6691b01d588a0c935f055 100644 (file)
                tlu@2f000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x2f000 0x1000>;
-                       interupts = <61 2 >;
+                       interrupts = <61 2>;
                        interrupt-parent = <&mpic>;
                };
 
                tlu@15000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x15000 0x1000>;
-                       interupts = <75 2>;
+                       interrupts = <75 2>;
                        interrupt-parent = <&mpic>;
                };
        };
index 11dbda10d7563dbe574e2cb60e13b09cb0927f01..cd0ea2b993622a8fc389b24a3387b2be72932948 100644 (file)
                tlu@2f000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x2f000 0x1000>;
-                       interupts = <61 2 >;
+                       interrupts = <61 2>;
                        interrupt-parent = <&mpic>;
                };
 
                tlu@15000 {
                        compatible = "fsl,mpc8572-tlu", "fsl_tlu";
                        reg = <0x15000 0x1000>;
-                       interupts = <75 2>;
+                       interrupts = <75 2>;
                        interrupt-parent = <&mpic>;
                };
        };
index 5143228e3e5fe975a1d19eacbd2393b210622497..6636b1d7821b6e5d5bcd8126674a8f3d8499601f 100644 (file)
@@ -71,18 +71,32 @@ udelay:
        add     r4,r4,r5
        addi    r4,r4,-1
        divw    r4,r4,r5        /* BUS ticks */
+#ifdef CONFIG_8xx
+1:     mftbu   r5
+       mftb    r6
+       mftbu   r7
+#else
 1:     mfspr   r5, SPRN_TBRU
        mfspr   r6, SPRN_TBRL
        mfspr   r7, SPRN_TBRU
+#endif
        cmpw    0,r5,r7
        bne     1b              /* Get [synced] base time */
        addc    r9,r6,r4        /* Compute end time */
        addze   r8,r5
+#ifdef CONFIG_8xx
+2:     mftbu   r5
+#else
 2:     mfspr   r5, SPRN_TBRU
+#endif
        cmpw    0,r5,r8
        blt     2b
        bgt     3f
+#ifdef CONFIG_8xx
+       mftb    r6
+#else
        mfspr   r6, SPRN_TBRL
+#endif
        cmpw    0,r6,r9
        blt     2b
 3:     blr
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 9b198d1b3b2b0d04be214a899519f2c5ea1f8c48..856f8deb557ab9d0ef2fef9cff50d3cf67ddeb47 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 cc0655a702a739236d29168293478153385ecfc6..935b5e7a1436dbbead45f9e578f75a024de0366f 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 cca12f08484201aaf86a16920a2edcf7921a6fd5..894662a5d4d5c5aa25f43e30653609d80d901774 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 0c7f2bfcf1348100fb10c4cd6f74dcee34e42756..d8b600b3f058bda7e0358931b885b8c5d4f30215 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 851bac7afa4b26e25a860be03ab7fe0783541fca..1bd92fd43cfb7fc8a14f0bfeeaed62f50700f07d 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 fa19e2f1a874b3878d0cd556bc5c39417ff1a08d..4a594b76674d4e536ce714b40c183f598bf380d6 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 ce0ef6ce8f86a07ce74e6a11fcfaaa424a1e991c..c720e0b3238d2657ea6f45dc3e38529765be9fcc 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 86d638a3b359e1d0f8e03c09c08adafcf9a7aa28..bf0fa8b0a883f8ecac068d0b9376ec6e45a2de05 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 9039d3c97eecd2a5324ab759c7511d448ed1a49c..0bd9348a4db91264d5e8a104f17ed77afcd5f815 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 d3c1eb34c986470af2f7f4baa0bfc226b1ca1488..dd8f61510dfd01151b5d7035dd8cee7c30ac45a7 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 33283532e9d8fd8c14bbd376b3112136a134e233..237d1d25b44815d3c65c17465bc3c53bff3e1f5c 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 b15554a26c20b8fd6bd25896164e91f91e27c5a4..c8317fbf92c470f03842da5cf363efd97aed99b4 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 a5954cebbc5594228c655906da83714eb6c772cd..b6ea9e068c13dd483033a9f8aea2251d45084cac 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 f65e27b09bd38010218b270ebcb27adb967c277b..694012877bf7f1cfd1e1ea067b448f7e7c449e87 100644 (file)
@@ -16,6 +16,7 @@ struct vmemmap_backing {
        unsigned long phys;
        unsigned long virt_addr;
 };
+extern struct vmemmap_backing *vmemmap_list;
 
 /*
  * Functions that deal with pagetables that could be at any level of
@@ -91,7 +92,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 a63b045e707ce8a5d34c91e8fa698808a8ee4647..12c32c5f533d924428714301f7482493df00e3c8 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 3c1acc31a09280bdc8c706813879571587f3d75e..f595b98079ee1ec75064640001998a407524ad59 100644 (file)
@@ -366,6 +366,8 @@ BEGIN_FTR_SECTION_NESTED(96);               \
        cmpwi dest,0;                   \
        beq-  90b;                      \
 END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96)
+#elif defined(CONFIG_8xx)
+#define MFTB(dest)                     mftb dest
 #else
 #define MFTB(dest)                     mfspr dest, SPRN_TBRL
 #endif
index 7794b2b04eb2eceb04bda53adbda99b9c721f784..fc14a38c7ccffae6c8f7c233e2710462ef621e7d 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 0156702ba24e2bb4f8c9131627dac0e9f6406c7e..576ad88104cbb4596a3b389eb64e9eb6f3a31e5e 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 126f6e98f84de687d1d26d2ab83ff4b1d2a6fc86..fa8388ed94c52e6086110bc382c7196ea32f1ad0 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; \
 
 #else /* __powerpc64__ */
 
+#if defined(CONFIG_8xx)
+#define mftbl()                ({unsigned long rval;   \
+                       asm volatile("mftbl %0" : "=r" (rval)); rval;})
+#define mftbu()                ({unsigned long rval;   \
+                       asm volatile("mftbu %0" : "=r" (rval)); rval;})
+#else
 #define mftbl()                ({unsigned long rval;   \
                        asm volatile("mfspr %0, %1" : "=r" (rval) : \
                                "i" (SPRN_TBRL)); rval;})
 #define mftbu()                ({unsigned long rval;   \
                        asm volatile("mfspr %0, %1" : "=r" (rval) : \
                                "i" (SPRN_TBRU)); rval;})
+#endif
 #endif /* !__powerpc64__ */
 
 #define mttbl(v)       asm volatile("mttbl %0":: "r"(v))
index 703a8412dac28e2a567b4d1b4da5586ae890c34a..11ba86e176315e4ef260c8eb740ba52ca347c192 100644 (file)
@@ -26,6 +26,7 @@ extern void reloc_got2(unsigned long);
 void check_for_initrd(void);
 void do_init_bootmem(void);
 void setup_panic(void);
+#define ARCH_PANIC_TIMEOUT 180
 
 #endif /* !__ASSEMBLY__ */
 
index 98da78e0c2c0eeda19f1852faa725742aa43af76..084e0807db988e2a24b836df800406739f91a7e9 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 ba7b1973866e1933486f47ed59e3aa0efe799f34..9854c564ac525b02b9f81c363a1cd4477cd76d4b 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 18908caa1f3b3c0209e8324474f4ce2b6473da63..2cf846edb3fcc4e941e44a8fc032eecd789b22d7 100644 (file)
@@ -29,7 +29,11 @@ static inline cycles_t get_cycles(void)
        ret = 0;
 
        __asm__ __volatile__(
+#ifdef CONFIG_8xx
+               "97:    mftb %0\n"
+#else
                "97:    mfspr %0, %2\n"
+#endif
                "99:\n"
                ".section __ftr_fixup,\"a\"\n"
                ".align 2\n"
@@ -41,7 +45,11 @@ static inline cycles_t get_cycles(void)
                "       .long 0\n"
                "       .long 0\n"
                ".previous"
+#ifdef CONFIG_8xx
+               : "=r" (ret) : "i" (CPU_FTR_601));
+#else
                : "=r" (ret) : "i" (CPU_FTR_601), "i" (SPRN_TBRL));
+#endif
        return ret;
 #endif
 }
index 75c6ecdb8f3728a175bd61808cad34a9689fd47e..7422a999a39a5bfd8edbd674905417951a792a25 100644 (file)
@@ -36,9 +36,8 @@ typedef ppc_opcode_t uprobe_opcode_t;
 
 struct arch_uprobe {
        union {
-               u8      insn[MAX_UINSN_BYTES];
-               u8      ixol[MAX_UINSN_BYTES];
-               u32     ainsn;
+               u32     insn;
+               u32     ixol;
        };
 };
 
index 0fb1a6e9ff908c58517ccc991b4c6fcab5b534fe..6836ec79a8305a33005b8ca4ab17e3688307c6aa 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 e60a3697932caea31a7a0c2ab1c63f86c6c45240..2ea5cc033ec8b633febb50071c43cfd31958f4f3 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 67130206534717b285fb50c77d38e4557f053eae..4bd687d5e7aa3f80e1b33912b5451bd34a3187b1 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 d27c5afc90aecfbe41506814d3a0c3891bdacc4b..72d748b56c86b2b9ae960e49b819dfedef36f61a 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 3a9ed6ac224b323cf4908e3cfed19ec4ea03d613..9f905e40922e41de4eb6f43421290944c5d08567 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 e11863f4e595e2a3302e3ead79b439751ca5d402..847e40e62fcee8420046e153e5da9354bc80a40e 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 2156ea90eb54181c84bc5d1dff48b0de80d5e405..90fab64d911dcdca1cbcf4992e1bda00160db6a4 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 e1ec57e87b3b435b88ef55c4598d32c37c8e2e07..88a7fb458dfd50f0201d269d8177007cc382c62d 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/ftrace.h>
 
 #include <asm/machdep.h>
+#include <asm/pgalloc.h>
 #include <asm/prom.h>
 #include <asm/sections.h>
 
@@ -75,6 +76,17 @@ void arch_crash_save_vmcoreinfo(void)
 #ifndef CONFIG_NEED_MULTIPLE_NODES
        VMCOREINFO_SYMBOL(contig_page_data);
 #endif
+#if defined(CONFIG_PPC64) && defined(CONFIG_SPARSEMEM_VMEMMAP)
+       VMCOREINFO_SYMBOL(vmemmap_list);
+       VMCOREINFO_SYMBOL(mmu_vmemmap_psize);
+       VMCOREINFO_SYMBOL(mmu_psize_defs);
+       VMCOREINFO_STRUCT_SIZE(vmemmap_backing);
+       VMCOREINFO_OFFSET(vmemmap_backing, list);
+       VMCOREINFO_OFFSET(vmemmap_backing, phys);
+       VMCOREINFO_OFFSET(vmemmap_backing, virt_addr);
+       VMCOREINFO_STRUCT_SIZE(mmu_psize_def);
+       VMCOREINFO_OFFSET(mmu_psize_def, shift);
+#endif
 }
 
 /*
index fd82c289ab1c1c76c52c9fe147503d277870874c..28b898e681850ab22996a2043d46c867e96232be 100644 (file)
@@ -210,7 +210,7 @@ static void __init nvram_print_partitions(char * label)
        printk(KERN_WARNING "--------%s---------\n", label);
        printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n");
        list_for_each_entry(tmp_part, &nvram_partitions, partition) {
-               printk(KERN_WARNING "%4d    \t%02x\t%02x\t%d\t%12s\n",
+               printk(KERN_WARNING "%4d    \t%02x\t%02x\t%d\t%12.12s\n",
                       tmp_part->index, tmp_part->header.signature,
                       tmp_part->header.checksum, tmp_part->header.length,
                       tmp_part->header.name);
index 75c2d1009985eb8dbd9f3995fc900f9f21227d01..3386d8ab7eb0607b3c9d6f03e68824d4abe4bd88 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++) {
@@ -886,9 +890,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))
@@ -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 f3a47098fb8e90b31e98a7d2808f6e691064e363..fa0ad8aafbccf3950506a96a64f75bc2141f1bd7 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 b903dc5cf944aee99fc36629fdb974f3e26b6888..2b0da27eaee4242f156d37bfbb4efa06bad334df 100644 (file)
@@ -296,9 +296,6 @@ void __init setup_arch(char **cmdline_p)
        if (cpu_has_feature(CPU_FTR_UNIFIED_ID_CACHE))
                ucache_bsize = icache_bsize = dcache_bsize;
 
-       /* reboot on panic */
-       panic_timeout = 180;
-
        if (ppc_md.panic)
                setup_panic();
 
index 4085aaa9478fd90eff1f1ce149d661bc7488284f..856dd4e99bfe459cdb430f5cba6fbc0b3229be89 100644 (file)
@@ -588,9 +588,6 @@ void __init setup_arch(char **cmdline_p)
        dcache_bsize = ppc64_caches.dline_size;
        icache_bsize = ppc64_caches.iline_size;
 
-       /* reboot on panic */
-       panic_timeout = 180;
-
        if (ppc_md.panic)
                setup_panic();
 
index 749778e0a69d97dfded0e719066fb1fb4eebac71..68027bfa5f8e3c4a958deeb13db1b6a9fcf89325 100644 (file)
@@ -445,6 +445,12 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
 #endif /* CONFIG_ALTIVEC */
        if (copy_fpr_to_user(&frame->mc_fregs, current))
                return 1;
+
+       /*
+        * Clear the MSR VSX bit to indicate there is no valid state attached
+        * to this context, except in the specific case below where we set it.
+        */
+       msr &= ~MSR_VSX;
 #ifdef CONFIG_VSX
        /*
         * Copy VSR 0-31 upper half from thread_struct to local
index b3c615764c9b97bcb510d017bd9c8ff33e6d69ec..42991045349f815e124e18dba78c9a914c8b8b84 100644 (file)
@@ -122,6 +122,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
        flush_fp_to_thread(current);
        /* copy fpr regs and fpscr */
        err |= copy_fpr_to_user(&sc->fp_regs, current);
+
+       /*
+        * Clear the MSR VSX bit to indicate there is no valid state attached
+        * to this context, except in the specific case below where we set it.
+        */
+       msr &= ~MSR_VSX;
 #ifdef CONFIG_VSX
        /*
         * Copy VSX low doubleword to local buffer for formatting,
@@ -701,12 +707,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 +766,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 930cd8af35035441031e1abecbdde3472ab48808..a3b64f3bf9a298b057cfdc57b008ea01eebecf63 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 192b051df97e27e6a8a74b344151489af6f544d6..b3b144121cc99d64df0c409b0773259770ddf0af 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 62c3dd8c69f21d3d5373d7c79d0ea6ab1a69ed0f..907a472f9a9e4c1db3747a8d4ff22cc1bb133ee8 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 59f419b935f2501653379fd099b2b1b731a4e7d9..003b20964ea0dfa435af8e1aac8fadde534f1fc2 100644 (file)
@@ -186,7 +186,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
         * emulate_step() returns 1 if the insn was successfully emulated.
         * For all other cases, we need to single-step in hardware.
         */
-       ret = emulate_step(regs, auprobe->ainsn);
+       ret = emulate_step(regs, auprobe->insn);
        if (ret > 0)
                return true;
 
index 6b1f2a6d55178c445a4d1a657dc72b0a53ae7a4a..6b2b69616e7762507f3375300513834f328bb503 100644 (file)
@@ -232,9 +232,15 @@ __do_get_tspec:
        lwz     r6,(CFG_TB_ORIG_STAMP+4)(r9)
 
        /* Get a stable TB value */
+#ifdef CONFIG_8xx
+2:     mftbu   r3
+       mftbl   r4
+       mftbu   r0
+#else
 2:     mfspr   r3, SPRN_TBRU
        mfspr   r4, SPRN_TBRL
        mfspr   r0, SPRN_TBRU
+#endif
        cmplw   cr0,r3,r0
        bne-    2b
 
index 45ea281e9a21d479a36e47fbd94a3a0c65075bee..542c6f422e4d4a6288ef1cf18ac877c330ca4339 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 e7d0c88f621aa08425cb502b9e68b4cbf549ccd3..76a64821f4a23653b64cc6ba0e32daec509ee5b5 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 2f5c6b6d687709ecd7a71f0f7985a48974b075f5..93221e87b911963f890bc7e3a111359ead5054a5 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 35ec0a8547dabb86977f0e24657f8ba710cd4f69..92c9ab4bcfeced6583c62b38b2bbe914cbbe3945 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 ed03854481483afe5159a095fd8d8a2d6f042f2b..0deef1082e02715d6453f71ec2a049099a9cdbed 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 e593ff257bd300461d8a6e487e0fb448e5d86936..141b2027189a8add1c90dba3278c6727f7406798 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 6646c952c5e37a80c19d4d86296e7bbdc0e2f3ad..ce569b6bf4d8c0f7ad7f87b76a9b1effd9e573de 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 700df6f1d32c5a6ad88542db1a0c8ccd1bbe26d4..8912608b7e1b34908b2e86870366a4cee500a313 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 c8cefdd15fd8a2fa03661511e3ca6d2d7c730877..76a64ce6a5b6c641207cfa40b6dd421299f788a0 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 00e619bf608ef124c6b348e236afebff258089b5..3a0abd2e5a15a2b4b3e3c3305408790193358b24 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 7e345e00661a73065a85fc1c6d0a2e4ef7e44108..83da1f868fd5356dce92c721d963de0b794584f2 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 e5240524bf6c8aa8bbfc90cdadf5849d587e4d0b..0d513af62bba179ad7ccd777bb3f1aa4217b1cb3 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 043eec8461e779bfc6e1c25b678116d5f963a59d..f3ff587a8b7d58e064e6364606e98a0e6473f106 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 30c2f3b134c66c496c371de6e4a8c3bd6011dfbe..2c25f5412bdb7a080abb87f5baa22c45b00d691d 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 360ce68c98099f1cc3495107f112f4b19e82f56f..99d40f8977e8abcaaf54f499af4d15a1ae1f268e 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 7057a02f0906b2545540d92bef5ae2f45b6c527a..852989a9bad3040751d39a32c81b28bf28d864e4 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 62a2b5ab08eda0399bf009b81eebd36d587763d8..072287f1c3bc7347b6ff4f959843c97053fa4aa1 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 37f1cc417ca04a7879db0d52784fbafe1c54160f..928142c64cb00ed2ce0d30081222b66abc45ddc0 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 c71103b8a748350947ad3c2f6b7256c6c2841405..bc8de75b1925cd34ad1d11379fee8c09f1d275e5 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 17cfae5497a3384d756fb2ed63b775758e6efb19..f4dd041c14eac9400fb012beae92792fe9c9cfd1 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 da8b13c4b776fb5e3116d24ee2f380bed524ec72..5a1ab1250a056f26b395357abb4baf9222fb2c84 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 c0b48f96a91c9817b17e80b6b32f3f4e6aac0167..fe14ca3dd171cd60b3c07191fa3bdd78a141e2a6 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 da0e0bc268bd4bce1322b696dd25972c24bc740e..5efa97b993d899faf4d16e65fc4541a241e1b5a9 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 8f7633e3afb8112b0123a298211a31c82240dcf0..a38c4c9edab87aca16929b5e96377368b5016fbd 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 3219ba89524641587d2fbaca11624ac7034d6cc5..cf95cdef73c993f9b7e78aea998f275097ab0a60 100644 (file)
@@ -260,6 +260,7 @@ fail:
         */
        return rc;
 }
+EXPORT_SYMBOL_GPL(kvmppc_rtas_hcall);
 
 void kvmppc_rtas_tokens_free(struct kvm *kvm)
 {
index 1abe4788191ae15855305dc11fffa7486ceffd9b..bc50c97751d368b3f0b837030312dfc038890695 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 a3a5cb8ee7eac2baa68cfb809e6a4eebce5457a9..02a17dcf16107adbe1309185bf464d0fda4b8772 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 5133199f6cb7b6cab1feca199f434bfd69a89e1a..53e65a210b9a451ab1089bfe788277ddda071cf3 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 5fd1ba69357997381e8003b01d91fd13d81754d5..09bfd9bc7cf8d9b1db12a9afc1956ec2fa9fc5e3 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 ce6b73c29612532a9f8e32282b1347761a2aeb22..497b142f651c835f10c8395ec4f70297b18442c3 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 c2e5e98453a67e4609cf17007a74d20ed11fee2c..4fd9650eb0185374bfd567291dd16ce5f397b688 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 b10a01243abdeb788a7d3513b25a266cfc09d35c..89b7f821f6c41d84acfd997215b907047b0cd0a1 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 6d6f153b6c1d85f996d65056c328e172fb3bbc37..ebca6b88ea5e7d545840dc4f7b9c3efd2447d69f 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 c65593abae8eb879f71915cbf639f0dfec4468dc..ecf2247b13be771a0b1442a6d447fd5c881e89c1 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 19c8379575f70284a4e12cc56b1b8462ca3cd5e5..4132cd2fc1715c4a838be1c6fcee53018e6e2215 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 751cd45f65a0de5fcd23864e5dba7b82b1f8999a..2f9a0873b44fe21d106988004d2c48ae40ce210e 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 07c0106fab76104048664da03a82c309e7187013..9ae97686e9f444ded5700bd7621fd0a7207d08e4 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 e326489a54205b30bc8c111c442ca19ab6d5c831..2e0e67ef3544fd662dc7cb71b325311a0099b3d0 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 6936547018b89e21bbe63b2371c9607917fd66dd..c5f734e20b0fc3a885a81a82f41a0ac5cd93f94a 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 3bc700655fc88255ae39fe854341e467cd9910a5..74551b5e41e5156b0720c65ec9cdd1adf988260a 100644 (file)
@@ -117,6 +117,5 @@ void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
        struct hstate *hstate = hstate_file(vma->vm_file);
        unsigned long tsize = huge_page_shift(hstate) - 10;
 
-       __flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr, tsize, 0);
-
+       __flush_tlb_page(vma->vm_mm, vmaddr, tsize, 0);
 }
index 6c856fb8c15b01705fb6cf5ff28c0a232d85d3bd..5b9601715289c450a23e41b7ffc62d428496b8c9 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 536eec72c0f701584b717b86189d039442c54acb..9d95786aa80ffbdcb8601379d774fc1f158006d0 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 3e99c149271aa0d4f454c95a9d23eaffa7debec6..7ce9cf3b698835c0dd2b2644d137ff7549c68e72 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 41cd68dee68164c38f3436ee7a40e60326ecc8cb..358d743031385ae7a269ba65200483227126d372 100644 (file)
@@ -305,7 +305,7 @@ void __flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
 void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
 {
 #ifdef CONFIG_HUGETLB_PAGE
-       if (is_vm_hugetlb_page(vma))
+       if (vma && is_vm_hugetlb_page(vma))
                flush_hugetlb_page(vma, vmaddr);
 #endif
 
index c2a566fb8bb89cc816b8841fe47fcdf35b222836..bca2465a9c347ad65f3617a7a51f2e4f2a73034a 100644 (file)
@@ -403,3 +403,28 @@ config PPC_DOORBELL
        default n
 
 endmenu
+
+choice
+       prompt "Endianness selection"
+       default CPU_BIG_ENDIAN
+       help
+         This option selects whether a big endian or little endian kernel will
+         be built.
+
+config CPU_BIG_ENDIAN
+       bool "Build big endian kernel"
+       help
+         Build a big endian kernel.
+
+         If unsure, select this option.
+
+config CPU_LITTLE_ENDIAN
+       bool "Build little endian kernel"
+       help
+         Build a little endian kernel.
+
+         Note that if cross compiling a little endian kernel,
+         CROSS_COMPILE must point to a toolchain capable of targeting
+         little endian powerpc.
+
+endchoice
index fc536f2971c0c02697aa5b4a8e885955d437a68e..7553b6a77c64a0ec5a310cfce08d36c914e605c1 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 8844628915dc4770b8d9ce35f4a03ab9dee5e94c..1cb160dc1609a5be5dbfd17ff0bfcbeaf362829b 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 7fbc25b1813fe59ea80eceba65cb2a76027c0eec..ccb633e077b16d6c7a57bb6cfd4372ca87ceeb6a 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 356bc75ca74f6f0bf8f08dd6eca37b5c538dbfc3..4fca3def9db951896864d0b716d7dea8a2364ba2 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 057fc894be511342da4c914d28113b73a44752c0..7bfaf58d4664460db12c94499fc151e251f47f68 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 a702f1c0824292286bc008300886955844ffc380..72a102758d4e5f54e8540b5e78e6417f1a23da3d 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 1f97e2b87a62b85d30bf848a8dbab31938304f21..6f76ae417f47e561bbf7e8b01c588a8e73f6374d 100644 (file)
@@ -442,9 +442,35 @@ 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;
+       set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
 
        /* Discover PIC type and setup ppc_md accordingly */
        pseries_discover_pic();
@@ -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 5f997e79d570f8fb704cf9ec79ef49cb538b3b28..16a255255d3003c64ce8d517fe90bf8b9109552b 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 8ef53bc2e70e2e442962820382f4664b62aa7a34..aaa46b353715e94fd7eabb71f6227f3971524ba3 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 d18e6cc19df376eab16f7dcbcb09516f874ac289..a3c87f3957502bfe8713af0bf796b6b4ddaa417c 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 2d3b1dd9571da71aec4b11ab97ef5a0cc106be4f..9cd92e645028ecc8041ab856a5cf35621a2929c7 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 cb565bf93650ef7e49f1f57ae162c996b062b109..3f672980793817c72c8bad6f203ec65ed0472c4d 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 508ec8282b96f7e2d67641619d48d43f3917f808..a87b414c766af3f986b3575a37183e8ddb632255 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 8928507affead7aa3f9f128232af8d4822188331..6538b4de34fccdab9fbda2cbe5a93ebf161c1277 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 ddb6efe889144dd78e15e80150e7b8a75bbb2d89..58cd1f00e1efa8a02754c4855ac21b24a316a37e 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 f75d7e517927694c3782036c64de5842a4852d9d..5877e71901b345ef191a911f9ad0ba537abd9c8c 100644 (file)
@@ -101,7 +101,7 @@ config S390
        select GENERIC_CPU_DEVICES if !SMP
        select GENERIC_FIND_FIRST_BIT
        select GENERIC_SMP_IDLE_THREAD
-       select GENERIC_TIME_VSYSCALL_OLD
+       select GENERIC_TIME_VSYSCALL
        select HAVE_ALIGNED_STRUCT_PAGE if SLUB
        select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
        select HAVE_ARCH_SECCOMP_FILTER
@@ -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 f2737a005afcb760fa5e177d764f93d73607a985..9a42ecec564767411ff5eb506b4f2bb2e18e1107 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 46cae138ece2efa3617447d1fccf82fa504d4e7d..4363528dc8fd013492b2165f6978b5e5226657b1 100644 (file)
@@ -35,7 +35,6 @@ static u8 *ctrblk;
 static char keylen_flag;
 
 struct s390_aes_ctx {
-       u8 iv[AES_BLOCK_SIZE];
        u8 key[AES_MAX_KEY_SIZE];
        long enc;
        long dec;
@@ -441,30 +440,36 @@ static int cbc_aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
        return aes_set_key(tfm, in_key, key_len);
 }
 
-static int cbc_aes_crypt(struct blkcipher_desc *desc, long func, void *param,
+static int cbc_aes_crypt(struct blkcipher_desc *desc, long func,
                         struct blkcipher_walk *walk)
 {
+       struct s390_aes_ctx *sctx = crypto_blkcipher_ctx(desc->tfm);
        int ret = blkcipher_walk_virt(desc, walk);
        unsigned int nbytes = walk->nbytes;
+       struct {
+               u8 iv[AES_BLOCK_SIZE];
+               u8 key[AES_MAX_KEY_SIZE];
+       } param;
 
        if (!nbytes)
                goto out;
 
-       memcpy(param, walk->iv, AES_BLOCK_SIZE);
+       memcpy(param.iv, walk->iv, AES_BLOCK_SIZE);
+       memcpy(param.key, sctx->key, sctx->key_len);
        do {
                /* only use complete blocks */
                unsigned int n = nbytes & ~(AES_BLOCK_SIZE - 1);
                u8 *out = walk->dst.virt.addr;
                u8 *in = walk->src.virt.addr;
 
-               ret = crypt_s390_kmc(func, param, out, in, n);
+               ret = crypt_s390_kmc(func, &param, out, in, n);
                if (ret < 0 || ret != n)
                        return -EIO;
 
                nbytes &= AES_BLOCK_SIZE - 1;
                ret = blkcipher_walk_done(desc, walk, nbytes);
        } while ((nbytes = walk->nbytes));
-       memcpy(walk->iv, param, AES_BLOCK_SIZE);
+       memcpy(walk->iv, param.iv, AES_BLOCK_SIZE);
 
 out:
        return ret;
@@ -481,7 +486,7 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
                return fallback_blk_enc(desc, dst, src, nbytes);
 
        blkcipher_walk_init(&walk, dst, src, nbytes);
-       return cbc_aes_crypt(desc, sctx->enc, sctx->iv, &walk);
+       return cbc_aes_crypt(desc, sctx->enc, &walk);
 }
 
 static int cbc_aes_decrypt(struct blkcipher_desc *desc,
@@ -495,7 +500,7 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
                return fallback_blk_dec(desc, dst, src, nbytes);
 
        blkcipher_walk_init(&walk, dst, src, nbytes);
-       return cbc_aes_crypt(desc, sctx->dec, sctx->iv, &walk);
+       return cbc_aes_crypt(desc, sctx->dec, &walk);
 }
 
 static struct crypto_alg cbc_aes_alg = {
index 9b69c0befdcaafcb46565056e10c36e884ab8065..4e63f1a13600a4e126d470138e074d2e4e98682d 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 dc9200ca32eda4d3e5a819025b176f7af5a06d60..67026300c88e5565b1d52af8bbd3eb8fa387a62e 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 a908d2941c5d90ab305f8e7c490585e20113579a..b7eabaaeffbd09a1310c3a8de2fe73786f00169a 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 e87ecaa2c569860f0c9353fc3ed398a0eba034ad..d5bc3750616ebae0c2fedac6be4587238577b3ca 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 316c8503a3b4fda3824ca071065f145038b34a0c..114258eeaacdbbd796a07605e74c53d9610ba3be 100644 (file)
@@ -48,33 +48,21 @@ static inline void clear_page(void *page)
                : "memory", "cc");
 }
 
+/*
+ * copy_page uses the mvcl instruction with 0xb0 padding byte in order to
+ * bypass caches when copying a page. Especially when copying huge pages
+ * this keeps L1 and L2 data caches alive.
+ */
 static inline void copy_page(void *to, void *from)
 {
-       if (MACHINE_HAS_MVPG) {
-               register unsigned long reg0 asm ("0") = 0;
-               asm volatile(
-                       "       mvpg    %0,%1"
-                       : : "a" (to), "a" (from), "d" (reg0)
-                       : "memory", "cc");
-       } else
-               asm volatile(
-                       "       mvc     0(256,%0),0(%1)\n"
-                       "       mvc     256(256,%0),256(%1)\n"
-                       "       mvc     512(256,%0),512(%1)\n"
-                       "       mvc     768(256,%0),768(%1)\n"
-                       "       mvc     1024(256,%0),1024(%1)\n"
-                       "       mvc     1280(256,%0),1280(%1)\n"
-                       "       mvc     1536(256,%0),1536(%1)\n"
-                       "       mvc     1792(256,%0),1792(%1)\n"
-                       "       mvc     2048(256,%0),2048(%1)\n"
-                       "       mvc     2304(256,%0),2304(%1)\n"
-                       "       mvc     2560(256,%0),2560(%1)\n"
-                       "       mvc     2816(256,%0),2816(%1)\n"
-                       "       mvc     3072(256,%0),3072(%1)\n"
-                       "       mvc     3328(256,%0),3328(%1)\n"
-                       "       mvc     3584(256,%0),3584(%1)\n"
-                       "       mvc     3840(256,%0),3840(%1)\n"
-                       : : "a" (to), "a" (from) : "memory");
+       register void *reg2 asm ("2") = to;
+       register unsigned long reg3 asm ("3") = 0x1000;
+       register void *reg4 asm ("4") = from;
+       register unsigned long reg5 asm ("5") = 0xb0001000;
+       asm volatile(
+               "       mvcl    2,4"
+               : "+d" (reg2), "+d" (reg3), "+d" (reg4), "+d" (reg5)
+               : : "memory", "cc");
 }
 
 #define clear_user_page(page, vaddr, pg)       clear_page(page)
index 1cc185da9d38265aec129a0363941c630e47dfb1..c129ab2ac731c989503e0b61ee338332105a85f9 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 7dc7f9c63b65f35f62de8566de198c97333acef6..30ef748bc161eb3a7f52084ea763d54fe6ed09fc 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 df802ee14af6f76591cebdc54030488b42c3d0cc..94cfbe442f124cc720f2436c7e3c34d485cf0139 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 eb5f64d26d06190de4695d38e9190449859588df..10e0fcd3633d178f25b299a709b9c80706f05818 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 a73eb2e1e918351356005b99940629235ef6ee98..bc9746a7d47c53edf219c4b4b1f500bc0728a284 100644 (file)
@@ -26,8 +26,9 @@ struct vdso_data {
        __u64 wtom_clock_nsec;          /*                              0x28 */
        __u32 tz_minuteswest;           /* Minutes west of Greenwich    0x30 */
        __u32 tz_dsttime;               /* Type of dst correction       0x34 */
-       __u32 ectg_available;
-       __u32 ntp_mult;                 /* NTP adjusted multiplier      0x3C */
+       __u32 ectg_available;           /* ECTG instruction present     0x38 */
+       __u32 tk_mult;                  /* Mult. used for xtime_nsec    0x3c */
+       __u32 tk_shift;                 /* Shift used for xtime_nsec    0x40 */
 };
 
 struct vdso_per_cpu_data {
index 2416138ebd3e5fa5584d897737e27ab7e0978383..496116cd65ec8ef3a1b566ac8c1c0641dfcad5f4 100644 (file)
@@ -65,7 +65,8 @@ int main(void)
        DEFINE(__VDSO_WTOM_NSEC, offsetof(struct vdso_data, wtom_clock_nsec));
        DEFINE(__VDSO_TIMEZONE, offsetof(struct vdso_data, tz_minuteswest));
        DEFINE(__VDSO_ECTG_OK, offsetof(struct vdso_data, ectg_available));
-       DEFINE(__VDSO_NTP_MULT, offsetof(struct vdso_data, ntp_mult));
+       DEFINE(__VDSO_TK_MULT, offsetof(struct vdso_data, tk_mult));
+       DEFINE(__VDSO_TK_SHIFT, offsetof(struct vdso_data, tk_shift));
        DEFINE(__VDSO_ECTG_BASE, offsetof(struct vdso_per_cpu_data, ectg_timer_base));
        DEFINE(__VDSO_ECTG_USER, offsetof(struct vdso_per_cpu_data, ectg_user_time));
        /* constants used by the vdso */
index 6e24429784097c034d41072929e446a5ef49cab4..95e7ba0fbb7eb1323b45300c4f6afcae04103dc8 100644 (file)
@@ -194,7 +194,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
                return -EINVAL;
 
        /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
-       regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
+       regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) |
                (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 |
                (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_RI) << 32 |
                (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE);
index f45b2ab0cb81ae425ce2d72c2de9b6fa4fa241a3..d7658c4b2ed5817c75cd6a90c91b3a1b93738809 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 96543ac400a7820ede40f22bf9636753920456b7..fca20b5fe79e085e4795861edf7f4bb5736c67cb 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 59a9c35c4598ae3265c60f69f8f87ca0a1538c2d..bc71a7b95af546b94d6ac6ba74c6484bbb261069 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 4a460c44e17ec15763da0cf24207ed048fa929f9..813ec7260878662d097ba90ef04d90b6f183367d 100644 (file)
@@ -78,7 +78,7 @@ PGM_CHECK_DEFAULT                     /* 34 */
 PGM_CHECK_DEFAULT                      /* 35 */
 PGM_CHECK_DEFAULT                      /* 36 */
 PGM_CHECK_DEFAULT                      /* 37 */
-PGM_CHECK_DEFAULT                      /* 38 */
+PGM_CHECK_64BIT(do_dat_exception)      /* 38 */
 PGM_CHECK_64BIT(do_dat_exception)      /* 39 */
 PGM_CHECK_64BIT(do_dat_exception)      /* 3a */
 PGM_CHECK_64BIT(do_dat_exception)      /* 3b */
index ffe1c53264a708352622d0159f437f1e176d2a75..4444875266ee028bdcc4d43604a4dea591a1a47c 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 fb535874a2464853168c9a9182620acf10e37c74..d8fd508ccd1e180b7679d24939b253d31a56bc26 100644 (file)
@@ -94,7 +94,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
                return -EINVAL;
 
        /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
-       regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
+       regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) |
                (user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
        /* Check for invalid user address space control. */
        if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME)
index 064c3082ab33604c4d360d7267ecd5e0f6ac1ac1..dd95f1631621722ca9d35a5d67269b152cb630d2 100644 (file)
@@ -108,20 +108,10 @@ static void fixup_clock_comparator(unsigned long long delta)
        set_clock_comparator(S390_lowcore.clock_comparator);
 }
 
-static int s390_next_ktime(ktime_t expires,
+static int s390_next_event(unsigned long delta,
                           struct clock_event_device *evt)
 {
-       struct timespec ts;
-       u64 nsecs;
-
-       ts.tv_sec = ts.tv_nsec = 0;
-       monotonic_to_bootbased(&ts);
-       nsecs = ktime_to_ns(ktime_add(timespec_to_ktime(ts), expires));
-       do_div(nsecs, 125);
-       S390_lowcore.clock_comparator = sched_clock_base_cc + (nsecs << 9);
-       /* Program the maximum value if we have an overflow (== year 2042) */
-       if (unlikely(S390_lowcore.clock_comparator < sched_clock_base_cc))
-               S390_lowcore.clock_comparator = -1ULL;
+       S390_lowcore.clock_comparator = get_tod_clock() + delta;
        set_clock_comparator(S390_lowcore.clock_comparator);
        return 0;
 }
@@ -146,15 +136,14 @@ void init_cpu_timer(void)
        cpu = smp_processor_id();
        cd = &per_cpu(comparators, cpu);
        cd->name                = "comparator";
-       cd->features            = CLOCK_EVT_FEAT_ONESHOT |
-                                 CLOCK_EVT_FEAT_KTIME;
+       cd->features            = CLOCK_EVT_FEAT_ONESHOT;
        cd->mult                = 16777;
        cd->shift               = 12;
        cd->min_delta_ns        = 1;
        cd->max_delta_ns        = LONG_MAX;
        cd->rating              = 400;
        cd->cpumask             = cpumask_of(cpu);
-       cd->set_next_ktime      = s390_next_ktime;
+       cd->set_next_event      = s390_next_event;
        cd->set_mode            = s390_set_mode;
 
        clockevents_register_device(cd);
@@ -221,21 +210,30 @@ struct clocksource * __init clocksource_default_clock(void)
        return &clocksource_tod;
 }
 
-void update_vsyscall_old(struct timespec *wall_time, struct timespec *wtm,
-                       struct clocksource *clock, u32 mult)
+void update_vsyscall(struct timekeeper *tk)
 {
-       if (clock != &clocksource_tod)
+       u64 nsecps;
+
+       if (tk->clock != &clocksource_tod)
                return;
 
        /* Make userspace gettimeofday spin until we're done. */
        ++vdso_data->tb_update_count;
        smp_wmb();
-       vdso_data->xtime_tod_stamp = clock->cycle_last;
-       vdso_data->xtime_clock_sec = wall_time->tv_sec;
-       vdso_data->xtime_clock_nsec = wall_time->tv_nsec;
-       vdso_data->wtom_clock_sec = wtm->tv_sec;
-       vdso_data->wtom_clock_nsec = wtm->tv_nsec;
-       vdso_data->ntp_mult = mult;
+       vdso_data->xtime_tod_stamp = tk->clock->cycle_last;
+       vdso_data->xtime_clock_sec = tk->xtime_sec;
+       vdso_data->xtime_clock_nsec = tk->xtime_nsec;
+       vdso_data->wtom_clock_sec =
+               tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
+       vdso_data->wtom_clock_nsec = tk->xtime_nsec +
+               + (tk->wall_to_monotonic.tv_nsec << tk->shift);
+       nsecps = (u64) NSEC_PER_SEC << tk->shift;
+       while (vdso_data->wtom_clock_nsec >= nsecps) {
+               vdso_data->wtom_clock_nsec -= nsecps;
+               vdso_data->wtom_clock_sec++;
+       }
+       vdso_data->tk_mult = tk->mult;
+       vdso_data->tk_shift = tk->shift;
        smp_wmb();
        ++vdso_data->tb_update_count;
 }
index b2224e0b974ce5beff26c4d1e7838d48e0232ae9..5be8e472f57d1d0debe31c849b4177492380d957 100644 (file)
@@ -38,25 +38,26 @@ __kernel_clock_gettime:
        sl      %r1,__VDSO_XTIME_STAMP+4(%r5)
        brc     3,2f
        ahi     %r0,-1
-2:     ms      %r0,__VDSO_NTP_MULT(%r5)        /* cyc2ns(clock,cycle_delta) */
+2:     ms      %r0,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        lr      %r2,%r0
-       l       %r0,__VDSO_NTP_MULT(%r5)
+       l       %r0,__VDSO_TK_MULT(%r5)
        ltr     %r1,%r1
        mr      %r0,%r0
        jnm     3f
-       a       %r0,__VDSO_NTP_MULT(%r5)
+       a       %r0,__VDSO_TK_MULT(%r5)
 3:     alr     %r0,%r2
-       srdl    %r0,12
-       al      %r0,__VDSO_XTIME_NSEC(%r5)      /*  + xtime */
+       al      %r0,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
        al      %r1,__VDSO_XTIME_NSEC+4(%r5)
        brc     12,4f
        ahi     %r0,1
-4:     l       %r2,__VDSO_XTIME_SEC+4(%r5)
-       al      %r0,__VDSO_WTOM_NSEC(%r5)       /*  + wall_to_monotonic */
+4:     al      %r0,__VDSO_WTOM_NSEC(%r5)       /*  + wall_to_monotonic.nsec */
        al      %r1,__VDSO_WTOM_NSEC+4(%r5)
        brc     12,5f
        ahi     %r0,1
-5:     al      %r2,__VDSO_WTOM_SEC+4(%r5)
+5:     l       %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
+       srdl    %r0,0(%r2)                      /*  >> tk->shift */
+       l       %r2,__VDSO_XTIME_SEC+4(%r5)
+       al      %r2,__VDSO_WTOM_SEC+4(%r5)
        cl      %r4,__VDSO_UPD_COUNT+4(%r5)     /* check update counter */
        jne     1b
        basr    %r5,0
@@ -86,20 +87,21 @@ __kernel_clock_gettime:
        sl      %r1,__VDSO_XTIME_STAMP+4(%r5)
        brc     3,12f
        ahi     %r0,-1
-12:    ms      %r0,__VDSO_NTP_MULT(%r5)        /* cyc2ns(clock,cycle_delta) */
+12:    ms      %r0,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        lr      %r2,%r0
-       l       %r0,__VDSO_NTP_MULT(%r5)
+       l       %r0,__VDSO_TK_MULT(%r5)
        ltr     %r1,%r1
        mr      %r0,%r0
        jnm     13f
-       a       %r0,__VDSO_NTP_MULT(%r5)
+       a       %r0,__VDSO_TK_MULT(%r5)
 13:    alr     %r0,%r2
-       srdl    %r0,12
-       al      %r0,__VDSO_XTIME_NSEC(%r5)      /*  + xtime */
+       al      %r0,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
        al      %r1,__VDSO_XTIME_NSEC+4(%r5)
        brc     12,14f
        ahi     %r0,1
-14:    l       %r2,__VDSO_XTIME_SEC+4(%r5)
+14:    l       %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
+       srdl    %r0,0(%r2)                      /*  >> tk->shift */
+       l       %r2,__VDSO_XTIME_SEC+4(%r5)
        cl      %r4,__VDSO_UPD_COUNT+4(%r5)     /* check update counter */
        jne     11b
        basr    %r5,0
index 2d3633175e3be520850ad6b48b30e94c88058072..fd621a950f7c70e917ea6ca44bb4a5fd5b9869ab 100644 (file)
@@ -35,15 +35,14 @@ __kernel_gettimeofday:
        sl      %r1,__VDSO_XTIME_STAMP+4(%r5)
        brc     3,3f
        ahi     %r0,-1
-3:     ms      %r0,__VDSO_NTP_MULT(%r5)        /* cyc2ns(clock,cycle_delta) */
+3:     ms      %r0,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        st      %r0,24(%r15)
-       l       %r0,__VDSO_NTP_MULT(%r5)
+       l       %r0,__VDSO_TK_MULT(%r5)
        ltr     %r1,%r1
        mr      %r0,%r0
        jnm     4f
-       a       %r0,__VDSO_NTP_MULT(%r5)
+       a       %r0,__VDSO_TK_MULT(%r5)
 4:     al      %r0,24(%r15)
-       srdl    %r0,12
        al      %r0,__VDSO_XTIME_NSEC(%r5)      /*  + xtime */
        al      %r1,__VDSO_XTIME_NSEC+4(%r5)
        brc     12,5f
@@ -51,6 +50,8 @@ __kernel_gettimeofday:
 5:     mvc     24(4,%r15),__VDSO_XTIME_SEC+4(%r5)
        cl      %r4,__VDSO_UPD_COUNT+4(%r5)     /* check update counter */
        jne     1b
+       l       %r4,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
+       srdl    %r0,0(%r4)                      /*  >> tk->shift */
        l       %r4,24(%r15)                    /* get tv_sec from stack */
        basr    %r5,0
 6:     ltr     %r0,%r0
index d46c95ed5f19ae8c25f9255b32338e8cc3197906..0add1072ba306623665148816155f951f0baeeda 100644 (file)
@@ -34,14 +34,15 @@ __kernel_clock_gettime:
        tmll    %r4,0x0001                      /* pending update ? loop */
        jnz     0b
        stck    48(%r15)                        /* Store TOD clock */
+       lgf     %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
+       lg      %r0,__VDSO_XTIME_SEC(%r5)       /* tk->xtime_sec */
+       alg     %r0,__VDSO_WTOM_SEC(%r5)        /*  + wall_to_monotonic.sec */
        lg      %r1,48(%r15)
        sg      %r1,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
-       msgf    %r1,__VDSO_NTP_MULT(%r5)        /*  * NTP adjustment */
-       srlg    %r1,%r1,12                      /* cyc2ns(clock,cycle_delta) */
-       alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + xtime */
-       lg      %r0,__VDSO_XTIME_SEC(%r5)
-       alg     %r1,__VDSO_WTOM_NSEC(%r5)       /*  + wall_to_monotonic */
-       alg     %r0,__VDSO_WTOM_SEC(%r5)
+       msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
+       alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
+       alg     %r1,__VDSO_WTOM_NSEC(%r5)       /*  + wall_to_monotonic.nsec */
+       srlg    %r1,%r1,0(%r2)                  /*  >> tk->shift */
        clg     %r4,__VDSO_UPD_COUNT(%r5)       /* check update counter */
        jne     0b
        larl    %r5,13f
@@ -62,12 +63,13 @@ __kernel_clock_gettime:
        tmll    %r4,0x0001                      /* pending update ? loop */
        jnz     5b
        stck    48(%r15)                        /* Store TOD clock */
+       lgf     %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
        lg      %r1,48(%r15)
        sg      %r1,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
-       msgf    %r1,__VDSO_NTP_MULT(%r5)        /*  * NTP adjustment */
-       srlg    %r1,%r1,12                      /* cyc2ns(clock,cycle_delta) */
-       alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + xtime */
-       lg      %r0,__VDSO_XTIME_SEC(%r5)
+       msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
+       alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
+       srlg    %r1,%r1,0(%r2)                  /*  >> tk->shift */
+       lg      %r0,__VDSO_XTIME_SEC(%r5)       /* tk->xtime_sec */
        clg     %r4,__VDSO_UPD_COUNT(%r5)       /* check update counter */
        jne     5b
        larl    %r5,13f
index 36ee674722ec3a76b3439fe78752f024ba5ba434..d0860d1d0cccfafe86320a07380ea14ddff1a284 100644 (file)
@@ -31,12 +31,13 @@ __kernel_gettimeofday:
        stck    48(%r15)                        /* Store TOD clock */
        lg      %r1,48(%r15)
        sg      %r1,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
-       msgf    %r1,__VDSO_NTP_MULT(%r5)        /*  * NTP adjustment */
-       srlg    %r1,%r1,12                      /* cyc2ns(clock,cycle_delta) */
-       alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + xtime.tv_nsec */
-       lg      %r0,__VDSO_XTIME_SEC(%r5)       /* xtime.tv_sec */
+       msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
+       alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
+       lg      %r0,__VDSO_XTIME_SEC(%r5)       /* tk->xtime_sec */
        clg     %r4,__VDSO_UPD_COUNT(%r5)       /* check update counter */
        jne     0b
+       lgf     %r5,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
+       srlg    %r1,%r1,0(%r5)                  /*  >> tk->shift */
        larl    %r5,5f
 2:     clg     %r1,0(%r5)
        jl      3f
index 3a74d8af0d69dd2e3bac77a427a6bfd64723bae9..78d967f180f4adc088aea609df1cc664ad57b917 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 99d789e8a0189c0ab319d775b1c54beba0dac05b..374a439ccc6080a004c7593f6227bc0c799ff7a6 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 5ee56e5acc2396f594854dd8a3e0e99bc3b72720..5ddbbde6f65c32fde299b9b34adbddfb02c5886d 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 7f1f7ac5cf7f8a2c3f3966d4fe96fa23af90ea04..5f79d2d79ca76f34648677bb3514802458882b81 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 ed8064cb5c4921424d5981b890e6fd9b07f9ed02..569494e01ec658e1d7bfacac103727979fc7643d 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 dc99f1ca42678768e5e0241cbeaa20de4e0aae87..b44912a3294941b8df9e9ca27d140ce52149408d 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 59200ee275e568ae99d593484b5575be320b79b8..2440602e6df1e19ab442b3eafd6f18b6342792ec 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 97e03caf782598a20857ab276ff03df701c4c3f9..dbdab3e7a1a6266ca3b96cd692d298896bc9902f 100644 (file)
@@ -78,11 +78,14 @@ static size_t copy_in_kernel(size_t count, void __user *to,
  * contains the (negative) exception code.
  */
 #ifdef CONFIG_64BIT
+
 static unsigned long follow_table(struct mm_struct *mm,
                                  unsigned long address, int write)
 {
        unsigned long *table = (unsigned long *)__pa(mm->pgd);
 
+       if (unlikely(address > mm->context.asce_limit - 1))
+               return -0x38UL;
        switch (mm->context.asce_bits & _ASCE_TYPE_MASK) {
        case _ASCE_TYPE_REGION1:
                table = table + ((address >> 53) & 0x7ff);
index 0a2e5e086749c00b98bbcba84446377935fd87df..e794c88f699a4584a742a3006eb443e706e5c96e 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 0c9a17780e4b9be96288a75a356444761f0085d5..bf7c73d71eef1ee46ee9bcec9516be70fcc2dc27 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 84147984224a996fca36b72d3aff574df229f1c8..c747394029eec181dbe521775296c701e9054a62 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 278e671ec9ac22b977c0598aaa31552d3e32b567..800f064b0da7c9a32c3910ecefdfb3e6979d7f9b 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 716b3fd1d86397ea57389a106516eb12073dde43..2e067657db988d66d8fbd01912fbb4729d934ad3 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 1425cc034872e5993320e6722f4965bdb34f1189..656b7ada9326ab63ba45b42f3404cc39979e16f5 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 224f4bc9925ece7f38c85a145cf615aa78082569..9b0979f4df7a5331cdc07ea468c4e0a033510d53 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 1fa8be4097715360363c41f21043d184f68517fa..122f737a901fbad4e22de06652e6eb9b04abdfc8 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 21c5088788dae27eb8c98449f22e6cdf9f464bb4..b9d9489a50125b8d732299cf707b009a37617927 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 8c00785c60d5b2517dd83c3b75b7ac50b0d10e1d..a33673b3687df2fa1e76022635cc3961a60810c5 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 45a93669289da4d7ff53119d9d3ea69158fced7e..ad27ffa65e2eafd2fb89c959c812862b70ba8665 100644 (file)
@@ -41,8 +41,6 @@ struct thread_info {
 
 #endif
 
-#define PREEMPT_ACTIVE         0x10000000
-
 #if defined(CONFIG_4KSTACKS)
 #define THREAD_SHIFT   12
 #else
index 9b6e4beeb296261c2fb5b315377822e236c587cf..ca46834294b7fa9fbf7f99eb0958ae1c18af6df7 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 78c4fdb91bc57a425e9fd5978260088ef5072d34..d4f7a6a163dc26869160ed74017434f106909a4d 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 162007643cdcb94faac5ceec465c095a2520be51..ee93923b7f8252d8727515c5b1e0e57132bf6484 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 7c29fd1a87aa4289e78bb7297a4ce41c133cce63..f478ff1ddd028733fe735bcf619e77b8f02803ee 100644 (file)
@@ -14,6 +14,4 @@
 
 void ack_bad_irq(unsigned int irq);
 
-#define HARDIRQ_BITS   8
-
 #endif /* !(__SPARC64_HARDIRQ_H) */
index 76092c4dd2771cddfdad004ee8f07b6bd2488e3c..f668797ae234782c43279b5f85ed6b6ab2ce2c0b 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 e15538899f3df779170fa94f2940dec79d170d2e..aac53fcea807fcdc8683c3aaef3b3a47749c907c 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 36760317814fe4dcc2089aea5cb3b85772ecd211..8358dc144959aacc8b369830c21c130593b9a804 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 b99d4e4b6d284a6a93d6d9450de4f3da2e446b61..e5e1752d5d78e9a5ac594c698a78de432b78c9b1 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 dd3807599bb9ac63573191e1086e506699e5d41a..96efa7adc22330d2684e67ab3c83dafa99e89e8a 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 d5e5042510790a1927e5f8b9a234038a599bcab7..a5f01ac6d0f1a2619cdac6b639248cb5a7153dac 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 f0d6a9700f4c8351e20be4743d9782c590b9e016..3c3c89f52643e9ade6ce6b508b5be41915179102 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 e696432b950d9e5e528a1f79761cbfdc4e149275..2230f80d9fe326dc48576e83a721fcf106efeaf0 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 9c179fbfb219c12b20f49e23aaf44db5567840e7..140966fbd30369448de54e9ea56e4b9022577b17 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 53c0a82e60308d541271707681b046824ad79456..60b19f50c80a8f9d7f31b4b4105ed4fcded6a5ca 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 e72212148d2a9e08dfe680f8500186b263a721f1..1b0973503197508404967e8bb0ed58cc934b982e 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 fde5a419cf27e0fdd4173aa2993312928bc120ec..542e96ac4d39948c165bd63eb9a43e25c0ba72e4 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 bc4d3f5d2e5d1683bdfe6d0b33a4a60adcd21916..cb021453de2aaac00323a6ea6e5612e7132ff41f 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 baebab215492966718fc8846e9f251ae3d20af82..32a280ec38c1d6edef71b5576ce474221726ec1e 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 773c1f2983ce3282c59a8f93efd62663218e741f..c13c9f25d83a0b11dc1d415bd556c1cd25d6f41b 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 afa2a9e3d0a0c6734785dfaafc68538d7e393b51..39f0c662f4c81dcdf3050926a5a9525d352933a3 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 35923e8abd8234f8fe067c0ed4a5e6f49ee85cb8..cd91d010e6d3617456b7c743ee5efbb956c657d4 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 e142545244f2767c1baef029a1b3c3f7ccf0a8b3..b66a5338231e965252fef5f23234141bb2fc8f85 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 bde867fd71e8026529739fa49dd13dad369faa0c..e0c09bf85610117e1632a86cfd87ef828d286709 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 51561b8b15baf916f7d5cb5e5cc0db3d8eb6032f..beb0b5a5f21ff0cfaf77a48e221932d7a4b060f2 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 d950197a17e16229d0b62147b5956924d656baf4..87729fff13b96729cd6cadcc3825dba172ab1d7c 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 b3f833ab90ebf0c95d5af6e216e58c06abc9c6a5..4ced92f05358ef942bbdb5cefed92e44d131ab3a 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 a313e4a9399b0db7ad7ac597313760e7ba508418..14158d40ba766d05917482029edd93546078281d 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 8201c25e76697ad5f5a96de1b6921a3fb34372ac..3c1a7cb31579fd0fe55e68bced2dc11757734c35 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 0bacceb19150ebff3a9e9088b11b66d8f5d4490b..932ff90fd7602b3f44aeac291f3e56936a197af2 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 77e531f6c2a74343574da3e97a8383b91c3027da..46272dfc26e81e9ea4882b6789636f70bd8c5c47 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 4d2df328e51476cadc4cfef6f8eb636506f18d95..dd16c61f3263689f05b52d2da71f7c40685f1a0f 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 2ebec263d6859f317041d75d0172abde1fd4e4d5..69bb818fdd798b8d50dff44a46a9941272972a92 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 01ee23dd724d5812b7993245b8bcb15bb0a00a7a..c4d3da68b800db27e54761add314e4a882f4e4c6 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 96399646570a780e1b38a6324994c5f71470bb0b..30963178d7e940f4afe02ed86bc936020dd1ec8a 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 ed82edad1a392864dd3919ce2010b8a0474e8f15..5322e530d09cf9cbbb459198a1613997c5bf9964 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 0661aa606decec7480e4042aed430f8729af5bd5..5d3782deb403c4ffcdeab04cab86fde1e16194e6 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 5d721df48a72d10dfe56095e0a8cf67d258fd7f9..869023abe5a4440c3ec08777e987c7abcc9777b8 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 7a91f288c7081229a6c2d2a4a1385a9673c58fc5..ad3bf4b4324d94f10504947bd88b2dea93a44b2e 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 2cc3bce5ee914a158a16960c4ece028cc959b83a..3b3a360b429a8ac78b5089a98c09027aa40634a3 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 432aa0cb1b38caa75076938700c60761699dc920..b4f4733abc6ea8f9e6ef6abafbc75f0b16b08003 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 d45a2c48f1850d65fe01a0c23a91eea1115fc1dd..b3692ce78f9034773415503017c2a4cb24fa1d2c 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 822390f9a154a620768004d76124d9b7f266add1..54110af23985ab7a395d22d659c80e1ddc93f748 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 b8aa6df3e102d0cf7a2dd7ba7e4f8e9309e983d6..729aa107f64e6391cb435ff1f5c038065eafe3b2 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 4fd9ec0b58edcb57d0b3346d41eea54c6f66c35f..5e86eac4bfae572da1c65ce268b8572ce91a9c42 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 b9d7c4276682f3b20434c596f4ef457e300b90af..f10738d68b2d67eef3448258abb1d2b5024ee532 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 8ddea1f8006a5a0732a3550ff6a5883aabd7fdec..21ca44c4f6d58b1a845dfba592cbc704649bc1e3 100644 (file)
@@ -1,8 +1,3 @@
-config DEFCONFIG_LIST
-       string
-       option defconfig_list
-       default "arch/$ARCH/defconfig"
-
 config UML
        bool
        default y
index 133f7de2a13d5ade42c217c56c74604ae6a39a58..48d92bbe62e9eae2460df5fb99bdd83974b9aecb 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 3df3bd544492012ab9b78b0c43ab2648a1760e61..29880c9b324ed33601c8af02340a4765d9d75b3b 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 c03cd5a02364493fe83c81261eb45c0689bcfed8..d89b02bb6262d2bd34f21a96f8ec8f3845b5cd8c 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 2c8eeb2df8b43a6c21b4b10da99fe1042276229f..1c5b2a83046aab011e03c1e735b020a374608838 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 694c792bab4ec71b0829c110e59c6d774ec6223f..41c8c774ec1037ef6a64a34841729cd8c1c07f49 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 021104d98cb351d66f9f44353a6ddfa2da71b23d..75298d3358e7f3d2c7ff4e2b2c5ed8b81a8dca6d 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 7ddb64baf3270620ae608e4764407ccbfe36dd7b..8636e905426f195ab2e8e2600cfe719676f70178 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 bbcef522bcb18ad468fcb4c0d7738010b5e667ed..eecc4142764c7328108a76f5d02126281c44f845 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 0dc4d1c6f98a19b5ed018c58d7aeb683d468ae1c..4d6fdf68edf31cf31b993081a6d9432dff6e3c5b 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 5c3aef74237ffda72a0b252d4ee4b118d14a969a..974b87474a9900f1909f845d449eba90dc9b8338 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 87df5e3acc26eaa35798da9e8d2818ae2a6a5d70..016adf0985d522ccfb5f22e6d287bb60dfdb4e61 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 905924b773d345b8f49de2905e62a546f6461cd9..7b605e4dfffa91305e763e7698832edcbf80d3bd 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 82cdd8906f3d6ff70e3317a78ae00c08a0bed39e..a7ba27b2752be0923644eba19035ba3a97a6c792 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 0213e373a895ead3c412b24bf5b5f9b5d6a9aab2..2e02d1356fdfffd531305693399c378df7f1735d 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 818b4a1edb5b1bd26e3d8eb7baffe212df50a6f0..af36d8eabdf1c1dd6650e10682eff053550c906a 100644 (file)
@@ -117,12 +117,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
index 181108b8ecce766d0102b9e12ac070e6510337b0..0c6618e7189782674af77f9130ede7b3c71d39e6 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 6e3e1cb3f6a0030e5344147b8ceeae684d637ad9..e903c71f7e69822d2ce5240b957399dbab783037 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 7d6ba9db1be99696784aeba62343eeebff9c32dd..e0fc24db234ac1263c96fdc18d0bf4f553647198 100644 (file)
@@ -3,8 +3,9 @@
 #
 
 avx_supported := $(call as-instr,vpxor %xmm0$(comma)%xmm0$(comma)%xmm0,yes,no)
+avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
+                               $(comma)4)$(comma)%ymm2,yes,no)
 
-obj-$(CONFIG_CRYPTO_ABLK_HELPER_X86) += ablk_helper.o
 obj-$(CONFIG_CRYPTO_GLUE_HELPER_X86) += glue_helper.o
 
 obj-$(CONFIG_CRYPTO_AES_586) += aes-i586.o
index f80e668785c0bf5f36383e5ab49495c24f93f843..835488b745eed5ce9484a40dfe56bf839f51d33d 100644 (file)
@@ -34,7 +34,7 @@
 #include <asm/cpu_device_id.h>
 #include <asm/i387.h>
 #include <asm/crypto/aes.h>
-#include <asm/crypto/ablk_helper.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/internal/aead.h>
 #include <linux/workqueue.h>
index 414fe5d7946be077c25ba19160496c4bf910b1ab..4209a76fcdaad4225fb9c15fb954dfb5ad495c74 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
@@ -21,7 +22,6 @@
 #include <asm/xcr.h>
 #include <asm/xsave.h>
 #include <asm/crypto/camellia.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 #define CAMELLIA_AESNI_PARALLEL_BLOCKS 16
index 37fd0c0a81ea8861f30a649b01cee8a6c11db4e5..87a041a10f4ac1fe7cceccc82eb24fcb03f92fa4 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
@@ -21,7 +22,6 @@
 #include <asm/xcr.h>
 #include <asm/xsave.h>
 #include <asm/crypto/camellia.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 #define CAMELLIA_AESNI_PARALLEL_BLOCKS 16
index c6631813dc115c609e186044790aa5461cb6f0c7..e6a3700489b94119c514177f4a2a0b52e5423c13 100644 (file)
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/cast5.h>
 #include <crypto/cryptd.h>
 #include <crypto/ctr.h>
 #include <asm/xcr.h>
 #include <asm/xsave.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 #define CAST5_PARALLEL_BLOCKS 16
index 8d0dfb86a5593554e0d536a48c28572ecc92c91d..09f3677393e4b888895c83bae05d114df0d5184c 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/cast6.h>
 #include <crypto/cryptd.h>
@@ -37,7 +38,6 @@
 #include <crypto/xts.h>
 #include <asm/xcr.h>
 #include <asm/xsave.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 #define CAST6_PARALLEL_BLOCKS 8
index 23aabc6c20a5376fa81cf49ff9893ec76b6cdf05..2fae489b15246525991e6e606b8e01923e298263 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
@@ -22,7 +23,6 @@
 #include <asm/xcr.h>
 #include <asm/xsave.h>
 #include <asm/crypto/serpent-avx.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 #define SERPENT_AVX2_PARALLEL_BLOCKS 16
index 9ae83cf8d21e987e2e3bf9656a51ecaf61644427..ff487087097254f8368d035db08d02472dc9b76e 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/serpent.h>
 #include <crypto/cryptd.h>
@@ -38,7 +39,6 @@
 #include <asm/xcr.h>
 #include <asm/xsave.h>
 #include <asm/crypto/serpent-avx.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 /* 8-way parallel cipher functions */
index 97a356ece24d2b74d18090760e988c45d2bc914a..8c95f86373061680f4d8f4418d4da77f9123c111 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/serpent.h>
 #include <crypto/cryptd.h>
@@ -42,7 +43,6 @@
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
 #include <asm/crypto/serpent-sse2.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 
 static void serpent_decrypt_cbc_xway(void *ctx, u128 *dst, const u128 *src)
index 50226c4b86ed338da70cb285a53e1e86d210d5f4..f248546da1caa956014dfd9648814ad37f2ca8d3 100644 (file)
@@ -281,7 +281,7 @@ static int __init sha256_ssse3_mod_init(void)
        /* allow AVX to override SSSE3, it's a little faster */
        if (avx_usable()) {
 #ifdef CONFIG_AS_AVX2
-               if (boot_cpu_has(X86_FEATURE_AVX2))
+               if (boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_BMI2))
                        sha256_transform_asm = sha256_transform_rorx;
                else
 #endif
@@ -319,4 +319,4 @@ MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("SHA256 Secure Hash Algorithm, Supplemental SSE3 accelerated");
 
 MODULE_ALIAS("sha256");
-MODULE_ALIAS("sha384");
+MODULE_ALIAS("sha224");
index a62ba541884ef1a15da1082d9d2ca48296c563ec..4e3c665be1296f16cedde6f402249316b75bc4ed 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/types.h>
 #include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/ablk_helper.h>
 #include <crypto/algapi.h>
 #include <crypto/twofish.h>
 #include <crypto/cryptd.h>
@@ -39,7 +40,6 @@
 #include <asm/xcr.h>
 #include <asm/xsave.h>
 #include <asm/crypto/twofish.h>
-#include <asm/crypto/ablk_helper.h>
 #include <asm/crypto/glue_helper.h>
 #include <crypto/scatterwalk.h>
 #include <linux/workqueue.h>
index 15f960c06ff7ca23de88f7d72c48c83dbb489810..24ec1216596e9de4f57ebd1f75ebe7a95e77160f 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 c76ff74a98f2ed5ffcd72b4dcefbcb0c9203c0cd..ae5d7830855cb1f295dcbb2615499c9e8440cfe6 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 7d7443283a9d5b2dc19796a7d18e9bebd66e2164..947b5c417e830ae0ead192964ef07f8a68d88851 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 b4389a468fb6d91bf91ca4cbe0b54cdeaaba109b..c4412e972bbd4a876d969ff7b3991bd54d1e942c 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 be8269b00e2a1f720fe69755645948cf771e769e..d6b078e9fa28a3f4588237cb9a122f5b5ce53162 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.
diff --git a/arch/x86/include/asm/simd.h b/arch/x86/include/asm/simd.h
new file mode 100644 (file)
index 0000000..ee80b92
--- /dev/null
@@ -0,0 +1,11 @@
+
+#include <asm/i387.h>
+
+/*
+ * may_use_simd - whether it is allowable at this time to issue SIMD
+ *                instructions or access the SIMD register file
+ */
+static __must_check inline bool may_use_simd(void)
+{
+       return irq_fpu_usable();
+}
index c46a46be1ec699c0a92180c4f584da150fd7f5eb..3ba3de457d053e77eb36d7d6fe78f7769a1df470 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)
index 2874df24e7a448cd56631d87f5ac9e0fdd00cbc7..4cab890007a7267ecc7ce148eaa1fdfbee4a49df 100644 (file)
@@ -71,6 +71,17 @@ DEFINE_IRQ_VECTOR_EVENT(x86_platform_ipi);
  */
 DEFINE_IRQ_VECTOR_EVENT(irq_work);
 
+/*
+ * We must dis-allow sampling irq_work_exit() because perf event sampling
+ * itself can cause irq_work, which would lead to an infinite loop;
+ *
+ *  1) irq_work_exit happens
+ *  2) generates perf sample
+ *  3) generates irq_work
+ *  4) goto 1
+ */
+TRACE_EVENT_PERF_PERM(irq_work_exit, is_sampling_event(p_event) ? -EPERM : 0);
+
 /*
  * call_function - called when entering/exiting a call function interrupt
  * vector handler
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 5d9a3033b3d76dcf9d0d0566c11cfc0853d90c6f..d3a87780c70bdf017cc7ae92563a8f1af5e467a2 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 bb0465090ae53eb3246b5022d187dd4517bd39d0..37813b5ddc37472dba6c64b8ff3f2508dc085de0 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 ed165d657380062387d08da7bf2f607fff192490..d278736bf774f7d5c61bd64aa735f6de829b1075 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 47b56a7e99cbcf2e4387fd087cddbfb1c35060bc..6359506a19ee6bbc22ca634b42e0ecec689c86c3 100644 (file)
@@ -36,7 +36,7 @@ obj-$(CONFIG_CPU_SUP_AMD)             += perf_event_amd_iommu.o
 endif
 obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_p6.o perf_event_knc.o perf_event_p4.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
-obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_uncore.o
+obj-$(CONFIG_CPU_SUP_INTEL)            += perf_event_intel_uncore.o perf_event_intel_rapl.o
 endif
 
 
index 3daece79a142b5dca849468295c1d438cda592fa..bca023bdd6b2a8b4525d10145795408dee4fa01b 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 1414c90feabaa25014d6b4dfb5ecde1eafceb37f..0641113e296598c058cab739d94c0ae2f5280ec0 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)
diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
new file mode 100644 (file)
index 0000000..bf8e4a7
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * perf_event_intel_rapl.c: support Intel RAPL energy consumption counters
+ * Copyright (C) 2013 Google, Inc., Stephane Eranian
+ *
+ * Intel RAPL interface is specified in the IA-32 Manual Vol3b
+ * section 14.7.1 (September 2013)
+ *
+ * RAPL provides more controls than just reporting energy consumption
+ * however here we only expose the 3 energy consumption free running
+ * counters (pp0, pkg, dram).
+ *
+ * Each of those counters increments in a power unit defined by the
+ * RAPL_POWER_UNIT MSR. On SandyBridge, this unit is 1/(2^16) Joules
+ * but it can vary.
+ *
+ * Counter to rapl events mappings:
+ *
+ *  pp0 counter: consumption of all physical cores (power plane 0)
+ *       event: rapl_energy_cores
+ *    perf code: 0x1
+ *
+ *  pkg counter: consumption of the whole processor package
+ *       event: rapl_energy_pkg
+ *    perf code: 0x2
+ *
+ * dram counter: consumption of the dram domain (servers only)
+ *       event: rapl_energy_dram
+ *    perf code: 0x3
+ *
+ * We manage those counters as free running (read-only). They may be
+ * use simultaneously by other tools, such as turbostat.
+ *
+ * The events only support system-wide mode counting. There is no
+ * sampling support because it does not make sense and is not
+ * supported by the RAPL hardware.
+ *
+ * Because we want to avoid floating-point operations in the kernel,
+ * the events are all reported in fixed point arithmetic (32.32).
+ * Tools must adjust the counts to convert them to Watts using
+ * the duration of the measurement. Tools may use a function such as
+ * ldexp(raw_count, -32);
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/perf_event.h>
+#include <asm/cpu_device_id.h>
+#include "perf_event.h"
+
+/*
+ * RAPL energy status counters
+ */
+#define RAPL_IDX_PP0_NRG_STAT  0       /* all cores */
+#define INTEL_RAPL_PP0         0x1     /* pseudo-encoding */
+#define RAPL_IDX_PKG_NRG_STAT  1       /* entire package */
+#define INTEL_RAPL_PKG         0x2     /* pseudo-encoding */
+#define RAPL_IDX_RAM_NRG_STAT  2       /* DRAM */
+#define INTEL_RAPL_RAM         0x3     /* pseudo-encoding */
+
+/* Clients have PP0, PKG */
+#define RAPL_IDX_CLN   (1<<RAPL_IDX_PP0_NRG_STAT|\
+                        1<<RAPL_IDX_PKG_NRG_STAT)
+
+/* Servers have PP0, PKG, RAM */
+#define RAPL_IDX_SRV   (1<<RAPL_IDX_PP0_NRG_STAT|\
+                        1<<RAPL_IDX_PKG_NRG_STAT|\
+                        1<<RAPL_IDX_RAM_NRG_STAT)
+
+/*
+ * event code: LSB 8 bits, passed in attr->config
+ * any other bit is reserved
+ */
+#define RAPL_EVENT_MASK        0xFFULL
+
+#define DEFINE_RAPL_FORMAT_ATTR(_var, _name, _format)          \
+static ssize_t __rapl_##_var##_show(struct kobject *kobj,      \
+                               struct kobj_attribute *attr,    \
+                               char *page)                     \
+{                                                              \
+       BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE);             \
+       return sprintf(page, _format "\n");                     \
+}                                                              \
+static struct kobj_attribute format_attr_##_var =              \
+       __ATTR(_name, 0444, __rapl_##_var##_show, NULL)
+
+#define RAPL_EVENT_DESC(_name, _config)                                \
+{                                                              \
+       .attr   = __ATTR(_name, 0444, rapl_event_show, NULL),   \
+       .config = _config,                                      \
+}
+
+#define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */
+
+struct rapl_pmu {
+       spinlock_t       lock;
+       int              hw_unit;  /* 1/2^hw_unit Joule */
+       int              n_active; /* number of active events */
+       struct list_head active_list;
+       struct pmu       *pmu; /* pointer to rapl_pmu_class */
+       ktime_t          timer_interval; /* in ktime_t unit */
+       struct hrtimer   hrtimer;
+};
+
+static struct pmu rapl_pmu_class;
+static cpumask_t rapl_cpu_mask;
+static int rapl_cntr_mask;
+
+static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu);
+static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu_to_free);
+
+static inline u64 rapl_read_counter(struct perf_event *event)
+{
+       u64 raw;
+       rdmsrl(event->hw.event_base, raw);
+       return raw;
+}
+
+static inline u64 rapl_scale(u64 v)
+{
+       /*
+        * scale delta to smallest unit (1/2^32)
+        * users must then scale back: count * 1/(1e9*2^32) to get Joules
+        * or use ldexp(count, -32).
+        * Watts = Joules/Time delta
+        */
+       return v << (32 - __get_cpu_var(rapl_pmu)->hw_unit);
+}
+
+static u64 rapl_event_update(struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+       u64 prev_raw_count, new_raw_count;
+       s64 delta, sdelta;
+       int shift = RAPL_CNTR_WIDTH;
+
+again:
+       prev_raw_count = local64_read(&hwc->prev_count);
+       rdmsrl(event->hw.event_base, new_raw_count);
+
+       if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+                           new_raw_count) != prev_raw_count) {
+               cpu_relax();
+               goto again;
+       }
+
+       /*
+        * Now we have the new raw value and have updated the prev
+        * timestamp already. We can now calculate the elapsed delta
+        * (event-)time and add that to the generic event.
+        *
+        * Careful, not all hw sign-extends above the physical width
+        * of the count.
+        */
+       delta = (new_raw_count << shift) - (prev_raw_count << shift);
+       delta >>= shift;
+
+       sdelta = rapl_scale(delta);
+
+       local64_add(sdelta, &event->count);
+
+       return new_raw_count;
+}
+
+static void rapl_start_hrtimer(struct rapl_pmu *pmu)
+{
+       __hrtimer_start_range_ns(&pmu->hrtimer,
+                       pmu->timer_interval, 0,
+                       HRTIMER_MODE_REL_PINNED, 0);
+}
+
+static void rapl_stop_hrtimer(struct rapl_pmu *pmu)
+{
+       hrtimer_cancel(&pmu->hrtimer);
+}
+
+static enum hrtimer_restart rapl_hrtimer_handle(struct hrtimer *hrtimer)
+{
+       struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+       struct perf_event *event;
+       unsigned long flags;
+
+       if (!pmu->n_active)
+               return HRTIMER_NORESTART;
+
+       spin_lock_irqsave(&pmu->lock, flags);
+
+       list_for_each_entry(event, &pmu->active_list, active_entry) {
+               rapl_event_update(event);
+       }
+
+       spin_unlock_irqrestore(&pmu->lock, flags);
+
+       hrtimer_forward_now(hrtimer, pmu->timer_interval);
+
+       return HRTIMER_RESTART;
+}
+
+static void rapl_hrtimer_init(struct rapl_pmu *pmu)
+{
+       struct hrtimer *hr = &pmu->hrtimer;
+
+       hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       hr->function = rapl_hrtimer_handle;
+}
+
+static void __rapl_pmu_event_start(struct rapl_pmu *pmu,
+                                  struct perf_event *event)
+{
+       if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
+               return;
+
+       event->hw.state = 0;
+
+       list_add_tail(&event->active_entry, &pmu->active_list);
+
+       local64_set(&event->hw.prev_count, rapl_read_counter(event));
+
+       pmu->n_active++;
+       if (pmu->n_active == 1)
+               rapl_start_hrtimer(pmu);
+}
+
+static void rapl_pmu_event_start(struct perf_event *event, int mode)
+{
+       struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu->lock, flags);
+       __rapl_pmu_event_start(pmu, event);
+       spin_unlock_irqrestore(&pmu->lock, flags);
+}
+
+static void rapl_pmu_event_stop(struct perf_event *event, int mode)
+{
+       struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+       struct hw_perf_event *hwc = &event->hw;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu->lock, flags);
+
+       /* mark event as deactivated and stopped */
+       if (!(hwc->state & PERF_HES_STOPPED)) {
+               WARN_ON_ONCE(pmu->n_active <= 0);
+               pmu->n_active--;
+               if (pmu->n_active == 0)
+                       rapl_stop_hrtimer(pmu);
+
+               list_del(&event->active_entry);
+
+               WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+               hwc->state |= PERF_HES_STOPPED;
+       }
+
+       /* check if update of sw counter is necessary */
+       if ((mode & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+               /*
+                * Drain the remaining delta count out of a event
+                * that we are disabling:
+                */
+               rapl_event_update(event);
+               hwc->state |= PERF_HES_UPTODATE;
+       }
+
+       spin_unlock_irqrestore(&pmu->lock, flags);
+}
+
+static int rapl_pmu_event_add(struct perf_event *event, int mode)
+{
+       struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu);
+       struct hw_perf_event *hwc = &event->hw;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu->lock, flags);
+
+       hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+       if (mode & PERF_EF_START)
+               __rapl_pmu_event_start(pmu, event);
+
+       spin_unlock_irqrestore(&pmu->lock, flags);
+
+       return 0;
+}
+
+static void rapl_pmu_event_del(struct perf_event *event, int flags)
+{
+       rapl_pmu_event_stop(event, PERF_EF_UPDATE);
+}
+
+static int rapl_pmu_event_init(struct perf_event *event)
+{
+       u64 cfg = event->attr.config & RAPL_EVENT_MASK;
+       int bit, msr, ret = 0;
+
+       /* only look at RAPL events */
+       if (event->attr.type != rapl_pmu_class.type)
+               return -ENOENT;
+
+       /* check only supported bits are set */
+       if (event->attr.config & ~RAPL_EVENT_MASK)
+               return -EINVAL;
+
+       /*
+        * check event is known (determines counter)
+        */
+       switch (cfg) {
+       case INTEL_RAPL_PP0:
+               bit = RAPL_IDX_PP0_NRG_STAT;
+               msr = MSR_PP0_ENERGY_STATUS;
+               break;
+       case INTEL_RAPL_PKG:
+               bit = RAPL_IDX_PKG_NRG_STAT;
+               msr = MSR_PKG_ENERGY_STATUS;
+               break;
+       case INTEL_RAPL_RAM:
+               bit = RAPL_IDX_RAM_NRG_STAT;
+               msr = MSR_DRAM_ENERGY_STATUS;
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* check event supported */
+       if (!(rapl_cntr_mask & (1 << bit)))
+               return -EINVAL;
+
+       /* unsupported modes and filters */
+       if (event->attr.exclude_user   ||
+           event->attr.exclude_kernel ||
+           event->attr.exclude_hv     ||
+           event->attr.exclude_idle   ||
+           event->attr.exclude_host   ||
+           event->attr.exclude_guest  ||
+           event->attr.sample_period) /* no sampling */
+               return -EINVAL;
+
+       /* must be done before validate_group */
+       event->hw.event_base = msr;
+       event->hw.config = cfg;
+       event->hw.idx = bit;
+
+       return ret;
+}
+
+static void rapl_pmu_event_read(struct perf_event *event)
+{
+       rapl_event_update(event);
+}
+
+static ssize_t rapl_get_attr_cpumask(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &rapl_cpu_mask);
+
+       buf[n++] = '\n';
+       buf[n] = '\0';
+       return n;
+}
+
+static DEVICE_ATTR(cpumask, S_IRUGO, rapl_get_attr_cpumask, NULL);
+
+static struct attribute *rapl_pmu_attrs[] = {
+       &dev_attr_cpumask.attr,
+       NULL,
+};
+
+static struct attribute_group rapl_pmu_attr_group = {
+       .attrs = rapl_pmu_attrs,
+};
+
+EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01");
+EVENT_ATTR_STR(energy-pkg  , rapl_pkg, "event=0x02");
+EVENT_ATTR_STR(energy-ram  , rapl_ram, "event=0x03");
+
+EVENT_ATTR_STR(energy-cores.unit, rapl_cores_unit, "Joules");
+EVENT_ATTR_STR(energy-pkg.unit  , rapl_pkg_unit, "Joules");
+EVENT_ATTR_STR(energy-ram.unit  , rapl_ram_unit, "Joules");
+
+/*
+ * we compute in 0.23 nJ increments regardless of MSR
+ */
+EVENT_ATTR_STR(energy-cores.scale, rapl_cores_scale, "2.3283064365386962890625e-10");
+EVENT_ATTR_STR(energy-pkg.scale, rapl_pkg_scale, "2.3283064365386962890625e-10");
+EVENT_ATTR_STR(energy-ram.scale, rapl_ram_scale, "2.3283064365386962890625e-10");
+
+static struct attribute *rapl_events_srv_attr[] = {
+       EVENT_PTR(rapl_cores),
+       EVENT_PTR(rapl_pkg),
+       EVENT_PTR(rapl_ram),
+
+       EVENT_PTR(rapl_cores_unit),
+       EVENT_PTR(rapl_pkg_unit),
+       EVENT_PTR(rapl_ram_unit),
+
+       EVENT_PTR(rapl_cores_scale),
+       EVENT_PTR(rapl_pkg_scale),
+       EVENT_PTR(rapl_ram_scale),
+       NULL,
+};
+
+static struct attribute *rapl_events_cln_attr[] = {
+       EVENT_PTR(rapl_cores),
+       EVENT_PTR(rapl_pkg),
+
+       EVENT_PTR(rapl_cores_unit),
+       EVENT_PTR(rapl_pkg_unit),
+
+       EVENT_PTR(rapl_cores_scale),
+       EVENT_PTR(rapl_pkg_scale),
+       NULL,
+};
+
+static struct attribute_group rapl_pmu_events_group = {
+       .name = "events",
+       .attrs = NULL, /* patched at runtime */
+};
+
+DEFINE_RAPL_FORMAT_ATTR(event, event, "config:0-7");
+static struct attribute *rapl_formats_attr[] = {
+       &format_attr_event.attr,
+       NULL,
+};
+
+static struct attribute_group rapl_pmu_format_group = {
+       .name = "format",
+       .attrs = rapl_formats_attr,
+};
+
+const struct attribute_group *rapl_attr_groups[] = {
+       &rapl_pmu_attr_group,
+       &rapl_pmu_format_group,
+       &rapl_pmu_events_group,
+       NULL,
+};
+
+static struct pmu rapl_pmu_class = {
+       .attr_groups    = rapl_attr_groups,
+       .task_ctx_nr    = perf_invalid_context, /* system-wide only */
+       .event_init     = rapl_pmu_event_init,
+       .add            = rapl_pmu_event_add, /* must have */
+       .del            = rapl_pmu_event_del, /* must have */
+       .start          = rapl_pmu_event_start,
+       .stop           = rapl_pmu_event_stop,
+       .read           = rapl_pmu_event_read,
+};
+
+static void rapl_cpu_exit(int cpu)
+{
+       struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
+       int i, phys_id = topology_physical_package_id(cpu);
+       int target = -1;
+
+       /* find a new cpu on same package */
+       for_each_online_cpu(i) {
+               if (i == cpu)
+                       continue;
+               if (phys_id == topology_physical_package_id(i)) {
+                       target = i;
+                       break;
+               }
+       }
+       /*
+        * clear cpu from cpumask
+        * if was set in cpumask and still some cpu on package,
+        * then move to new cpu
+        */
+       if (cpumask_test_and_clear_cpu(cpu, &rapl_cpu_mask) && target >= 0)
+               cpumask_set_cpu(target, &rapl_cpu_mask);
+
+       WARN_ON(cpumask_empty(&rapl_cpu_mask));
+       /*
+        * migrate events and context to new cpu
+        */
+       if (target >= 0)
+               perf_pmu_migrate_context(pmu->pmu, cpu, target);
+
+       /* cancel overflow polling timer for CPU */
+       rapl_stop_hrtimer(pmu);
+}
+
+static void rapl_cpu_init(int cpu)
+{
+       int i, phys_id = topology_physical_package_id(cpu);
+
+       /* check if phys_is is already covered */
+       for_each_cpu(i, &rapl_cpu_mask) {
+               if (phys_id == topology_physical_package_id(i))
+                       return;
+       }
+       /* was not found, so add it */
+       cpumask_set_cpu(cpu, &rapl_cpu_mask);
+}
+
+static int rapl_cpu_prepare(int cpu)
+{
+       struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
+       int phys_id = topology_physical_package_id(cpu);
+       u64 ms;
+
+       if (pmu)
+               return 0;
+
+       if (phys_id < 0)
+               return -1;
+
+       pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu));
+       if (!pmu)
+               return -1;
+
+       spin_lock_init(&pmu->lock);
+
+       INIT_LIST_HEAD(&pmu->active_list);
+
+       /*
+        * grab power unit as: 1/2^unit Joules
+        *
+        * we cache in local PMU instance
+        */
+       rdmsrl(MSR_RAPL_POWER_UNIT, pmu->hw_unit);
+       pmu->hw_unit = (pmu->hw_unit >> 8) & 0x1FULL;
+       pmu->pmu = &rapl_pmu_class;
+
+       /*
+        * use reference of 200W for scaling the timeout
+        * to avoid missing counter overflows.
+        * 200W = 200 Joules/sec
+        * divide interval by 2 to avoid lockstep (2 * 100)
+        * if hw unit is 32, then we use 2 ms 1/200/2
+        */
+       if (pmu->hw_unit < 32)
+               ms = (1000 / (2 * 100)) * (1ULL << (32 - pmu->hw_unit - 1));
+       else
+               ms = 2;
+
+       pmu->timer_interval = ms_to_ktime(ms);
+
+       rapl_hrtimer_init(pmu);
+
+       /* set RAPL pmu for this cpu for now */
+       per_cpu(rapl_pmu, cpu) = pmu;
+       per_cpu(rapl_pmu_to_free, cpu) = NULL;
+
+       return 0;
+}
+
+static void rapl_cpu_kfree(int cpu)
+{
+       struct rapl_pmu *pmu = per_cpu(rapl_pmu_to_free, cpu);
+
+       kfree(pmu);
+
+       per_cpu(rapl_pmu_to_free, cpu) = NULL;
+}
+
+static int rapl_cpu_dying(int cpu)
+{
+       struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
+
+       if (!pmu)
+               return 0;
+
+       per_cpu(rapl_pmu, cpu) = NULL;
+
+       per_cpu(rapl_pmu_to_free, cpu) = pmu;
+
+       return 0;
+}
+
+static int rapl_cpu_notifier(struct notifier_block *self,
+                            unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (long)hcpu;
+
+       switch (action & ~CPU_TASKS_FROZEN) {
+       case CPU_UP_PREPARE:
+               rapl_cpu_prepare(cpu);
+               break;
+       case CPU_STARTING:
+               rapl_cpu_init(cpu);
+               break;
+       case CPU_UP_CANCELED:
+       case CPU_DYING:
+               rapl_cpu_dying(cpu);
+               break;
+       case CPU_ONLINE:
+       case CPU_DEAD:
+               rapl_cpu_kfree(cpu);
+               break;
+       case CPU_DOWN_PREPARE:
+               rapl_cpu_exit(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static const struct x86_cpu_id rapl_cpu_match[] = {
+       [0] = { .vendor = X86_VENDOR_INTEL, .family = 6 },
+       [1] = {},
+};
+
+static int __init rapl_pmu_init(void)
+{
+       struct rapl_pmu *pmu;
+       int cpu, ret;
+
+       /*
+        * check for Intel processor family 6
+        */
+       if (!x86_match_cpu(rapl_cpu_match))
+               return 0;
+
+       /* check supported CPU */
+       switch (boot_cpu_data.x86_model) {
+       case 42: /* Sandy Bridge */
+       case 58: /* Ivy Bridge */
+       case 60: /* Haswell */
+               rapl_cntr_mask = RAPL_IDX_CLN;
+               rapl_pmu_events_group.attrs = rapl_events_cln_attr;
+               break;
+       case 45: /* Sandy Bridge-EP */
+       case 62: /* IvyTown */
+               rapl_cntr_mask = RAPL_IDX_SRV;
+               rapl_pmu_events_group.attrs = rapl_events_srv_attr;
+               break;
+
+       default:
+               /* unsupported */
+               return 0;
+       }
+       get_online_cpus();
+
+       for_each_online_cpu(cpu) {
+               rapl_cpu_prepare(cpu);
+               rapl_cpu_init(cpu);
+       }
+
+       perf_cpu_notifier(rapl_cpu_notifier);
+
+       ret = perf_pmu_register(&rapl_pmu_class, "power", -1);
+       if (WARN_ON(ret)) {
+               pr_info("RAPL PMU detected, registration failed (%d), RAPL PMU disabled\n", ret);
+               put_online_cpus();
+               return -1;
+       }
+
+       pmu = __get_cpu_var(rapl_pmu);
+
+       pr_info("RAPL PMU detected, hw unit 2^-%d Joules,"
+               " API unit is 2^-32 Joules,"
+               " %d fixed counters"
+               " %llu ms ovfl timer\n",
+               pmu->hw_unit,
+               hweight32(rapl_cntr_mask),
+               ktime_to_ms(pmu->timer_interval));
+
+       put_online_cpus();
+
+       return 0;
+}
+device_initcall(rapl_pmu_init);
index f2cc63e9cf08f94f50b5f618e1c07b438ac5071e..b6f794aa16937f5c3e2c09991679a75c55bf8249 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 b3cd3ebae0774001391548d7cad0582fcbfeb50d..bc4a088f902396721e08a2d7314a451b7597eb1c 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 = {
@@ -320,8 +330,8 @@ static struct pci_device_id intel_stolen_ids[] __initdata = {
        INTEL_I915GM_IDS(gen3_stolen_size),
        INTEL_I945G_IDS(gen3_stolen_size),
        INTEL_I945GM_IDS(gen3_stolen_size),
-       INTEL_VLV_M_IDS(gen3_stolen_size),
-       INTEL_VLV_D_IDS(gen3_stolen_size),
+       INTEL_VLV_M_IDS(gen6_stolen_size),
+       INTEL_VLV_D_IDS(gen6_stolen_size),
        INTEL_PINEVIEW_IDS(gen3_stolen_size),
        INTEL_I965G_IDS(gen3_stolen_size),
        INTEL_G33_IDS(gen3_stolen_size),
@@ -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 42a392a9fd02fd3c0e306e3a4f4da323444dc3fd..d4bdd253fea71358ca080ba567a44935a9830396 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 1570e07413445845691c4850c7aa6348a4f371af..e6041094ff265b48383b2e1e67b63a931f99032a 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 a16bae3f83b37ab189dbe2a443f1461a075a6ae7..2f355d229a587771680b28080d92fd06f345d7e7 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 a47a3e54b964b5bd486e2d199816fb12acdc9170..b89c5db2b8325a6398baaed5c5c076d223599bf5 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 bf4fb04d011217bb13ab86661866c859ec790261..25d22b2d6509e3e0924c39641214085de2aa2b52 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 b110fe6c03d43908146d05ad689937d3bd991bb9..c6976257eff51281e023c264b355f166ce56dd5c 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 b7fd07984888e9f348c04df9043c6596928c1cd3..f1e4895174b2472da123b8b74cbee2460b239ccf 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 ddc3f3d2afdb155b3ce39f4049c800356fb42d04..07ffca0a89e945b8dafbd7ce5a237652da597334 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 dce0df8150df23709607ca05ef4d09cb867cc035..40772ef0f2b12458f21094d9a642a487a081fdde 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 77e044a0f5f70f36222510cf8012ecb9607da65e..292615274358ee33a1afeb1eef159901ae6f164e 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 daff69e21150d054a109a889630f730702088b76..1185fe7a7f47b053ba3c0fcb1b079d1614581e57 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 c0bc80391e40a9bd1dfbf97243160ddcef61f145..c7168a5cff1b33ad454cc85176695cb0c7394dfc 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 2b2fce1b200900b1af42865f946d5faa25fdc56a..b2fe1c252f35f92e3b6707406998738a9275c8d2 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 e5ca72a5cdb6da13617033ad8c0c65c4391d9e2f..21ef1ba184ae8500a70061f566ea55fde76cbfd2 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 e224f7a671b66fb23a291cdf25c5eda7f4bdd157..587fb9ede436076050b71592a0eb1ee562dc8517 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 dfa537a03be1e187fe8772b83a44639600d1cdf4..c96314abd144ca91cfcccaba72352ad0fdc6cf5b 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 7fb24e53d4c8b88b1fd374a327e0535269f7e328..4f25ec0775526f45133d9087bc13fd4f76dfb24e 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 14ef8d1dbc33bf954b062b525ed0771ef4489172..ed56a1c4ae734d8fa2b3cb2ccd4133c2ebc4c5a8 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 6c6689e574ce9057d5143b2f3196f9f11418f517..c112de81c9e19860fe59087a653697a5d2afb5e1 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 4b02a8455bd1eb55056149630c596f9e4ea9dfd1..c3be85205a6599402d4343c8c2554bb9722a5e54 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 c9bee5b8c0d3e84e7d42cef757c7c74906514e28..16ee0e450e3e39df58bcb6c7dca97b46cb01b443 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 a0e7fb1134a074c292b248eef68f952aba755581..38b4e4abd0f836b28ee6596bf2aa2410078a6df4 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 fdc3ba28ca38d2a89eb3e59e6bca73b692f84945..ce563be09cc19ebe97b499804c4d2a765d6d7fb3 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 a61c7d5811beac47e2549cd6fd44118bc5bf7b28..2ae8699e8767b05bd6398fbd9ef69168b57d132a 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 969570491c3964d0023dc82a231ff683ee88f735..0e98e5d241d04847c7a9a54c242b7a232ebae148 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 09f3059cb00bf38061aa1f8728916ad1b38ea5b2..68c054f59de686657e39879e5fc4b93e6a8aec71 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 31d04758b76f6a2da41c309e368768d4881066d0..c36b325abd83c6e48ac25fe1af66541f643c2f9b 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 be6b8607895748304c860c61603d24a29bacfa34..0e36cde12f7e7de605d676055897f04bb231d654 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 ee365895b06bb431a2f56e1f940119a9c0cc5ef1..12a1ca707b94258466122b5f2d793726f8185d54 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 cf914c8c249a0b801efa0e6e5768c5c1392ec926..d38eb9237e645cf5affd4964d8d289abb1a2ee4f 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 0fdf5d043f82ab16a3f1b50766101392cadd483f..216446295ada686ccb4b454319b2b45e85d96720 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 9481004ac119e04ef8f6a80921561de071b445a1..470153e8547c19a6819ac4cfbea3aa095b3dd33e 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 a1077570e38341808c9316c6eb03268552e26820..c43771c974be94ceea2152bf77cec9777b3c4b1a 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 46cd7bd18b347c580bfee1f4b86fb59321070e56..242df01413f6ee902b50b028b228b31330e0c1f2 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 f8ea39d7ae5466c9a36dd7ba9274c6acb8522fa5..0045ace9bdf0301f724463c2e37ae8b62e9902cb 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 88d4e864d4c0f512a5f858f01651f71074937ec3..cdc629cf075b74f27f7801565ec3d90f3e299ce0 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 ce4b8bfd3d279b0e6dc219d722420368bf969b25..57790c1a97ebb2eeea4f6d97b0c6ef42aa93a3a5 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 4f8c4d90ec7350613d8de5a815326a337e875a6e..97779522472f8356d5b09e91a33b1b310293d230 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 a8287b49d0621d1778295ad0516c8ccbf22ed0fa..dc51f467a560558ab4812d339fe2bd24f83a00b2 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 71f337aefa3905feaca892b7ac3b49b4bcb411e3..7bcb70d216e14b1b811e8924bb15cbf01f5acf80 100644 (file)
@@ -174,9 +174,8 @@ config CRYPTO_TEST
        help
          Quick & dirty crypto test module.
 
-config CRYPTO_ABLK_HELPER_X86
+config CRYPTO_ABLK_HELPER
        tristate
-       depends on X86
        select CRYPTO_CRYPTD
 
 config CRYPTO_GLUE_HELPER_X86
@@ -695,7 +694,7 @@ config CRYPTO_AES_NI_INTEL
        select CRYPTO_AES_X86_64 if 64BIT
        select CRYPTO_AES_586 if !64BIT
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_ALGAPI
        select CRYPTO_GLUE_HELPER_X86 if 64BIT
        select CRYPTO_LRW
@@ -895,7 +894,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX_X86_64
        depends on CRYPTO
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_CAMELLIA_X86_64
        select CRYPTO_LRW
@@ -917,7 +916,7 @@ config CRYPTO_CAMELLIA_AESNI_AVX2_X86_64
        depends on CRYPTO
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_CAMELLIA_X86_64
        select CRYPTO_CAMELLIA_AESNI_AVX_X86_64
@@ -969,7 +968,7 @@ config CRYPTO_CAST5_AVX_X86_64
        depends on X86 && 64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_CAST_COMMON
        select CRYPTO_CAST5
        help
@@ -992,7 +991,7 @@ config CRYPTO_CAST6_AVX_X86_64
        depends on X86 && 64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_CAST_COMMON
        select CRYPTO_CAST6
@@ -1110,7 +1109,7 @@ config CRYPTO_SERPENT_SSE2_X86_64
        depends on X86 && 64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_SERPENT
        select CRYPTO_LRW
@@ -1132,7 +1131,7 @@ config CRYPTO_SERPENT_SSE2_586
        depends on X86 && !64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_SERPENT
        select CRYPTO_LRW
@@ -1154,7 +1153,7 @@ config CRYPTO_SERPENT_AVX_X86_64
        depends on X86 && 64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_SERPENT
        select CRYPTO_LRW
@@ -1176,7 +1175,7 @@ config CRYPTO_SERPENT_AVX2_X86_64
        depends on X86 && 64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_SERPENT
        select CRYPTO_SERPENT_AVX_X86_64
@@ -1292,7 +1291,7 @@ config CRYPTO_TWOFISH_AVX_X86_64
        depends on X86 && 64BIT
        select CRYPTO_ALGAPI
        select CRYPTO_CRYPTD
-       select CRYPTO_ABLK_HELPER_X86
+       select CRYPTO_ABLK_HELPER
        select CRYPTO_GLUE_HELPER_X86
        select CRYPTO_TWOFISH_COMMON
        select CRYPTO_TWOFISH_X86_64
@@ -1402,6 +1401,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 80019ba8da3a2113ce8a48bf924bba9ca7d96e50..989c510da8cc98af911976874d6c16db865ee1b3 100644 (file)
@@ -2,8 +2,13 @@
 # Cryptographic API
 #
 
+# memneq MUST be built with -Os or -O0 to prevent early-return optimizations
+# that will defeat memneq's actual purpose to prevent timing attacks.
+CFLAGS_REMOVE_memneq.o := -O1 -O2 -O3
+CFLAGS_memneq.o := -Os
+
 obj-$(CONFIG_CRYPTO) += crypto.o
-crypto-y := api.o cipher.o compress.o
+crypto-y := api.o cipher.o compress.o memneq.o
 
 obj-$(CONFIG_CRYPTO_WORKQUEUE) += crypto_wq.o
 
@@ -104,3 +109,5 @@ 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
+obj-$(CONFIG_CRYPTO_ABLK_HELPER) += ablk_helper.o
similarity index 95%
rename from arch/x86/crypto/ablk_helper.c
rename to crypto/ablk_helper.c
index 43282fe04a8b726e57048d8e67fd96d209c76041..ffe7278d4bd83bd9b8ee5e7392af7bd417e037ae 100644 (file)
 #include <linux/crypto.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/hardirq.h>
 #include <crypto/algapi.h>
 #include <crypto/cryptd.h>
-#include <asm/i387.h>
-#include <asm/crypto/ablk_helper.h>
+#include <crypto/ablk_helper.h>
+#include <asm/simd.h>
 
 int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
                 unsigned int key_len)
@@ -70,11 +71,11 @@ int ablk_encrypt(struct ablkcipher_request *req)
        struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
        struct async_helper_ctx *ctx = crypto_ablkcipher_ctx(tfm);
 
-       if (!irq_fpu_usable()) {
+       if (!may_use_simd()) {
                struct ablkcipher_request *cryptd_req =
                        ablkcipher_request_ctx(req);
 
-               memcpy(cryptd_req, req, sizeof(*req));
+               *cryptd_req = *req;
                ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
 
                return crypto_ablkcipher_encrypt(cryptd_req);
@@ -89,11 +90,11 @@ int ablk_decrypt(struct ablkcipher_request *req)
        struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
        struct async_helper_ctx *ctx = crypto_ablkcipher_ctx(tfm);
 
-       if (!irq_fpu_usable()) {
+       if (!may_use_simd()) {
                struct ablkcipher_request *cryptd_req =
                        ablkcipher_request_ctx(req);
 
-               memcpy(cryptd_req, req, sizeof(*req));
+               *cryptd_req = *req;
                ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
 
                return crypto_ablkcipher_decrypt(cryptd_req);
index 7d4a8d28277e181386981dcef2d73ec377aefe9a..40886c489903b72a50c1b718869c38744d142aa4 100644 (file)
@@ -16,9 +16,7 @@
 #include <crypto/internal/skcipher.h>
 #include <linux/cpumask.h>
 #include <linux/err.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/rtnetlink.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -30,8 +28,6 @@
 
 #include "internal.h"
 
-static const char *skcipher_default_geniv __read_mostly;
-
 struct ablkcipher_buffer {
        struct list_head        entry;
        struct scatter_walk     dst;
@@ -527,8 +523,7 @@ const char *crypto_default_geniv(const struct crypto_alg *alg)
            alg->cra_blocksize)
                return "chainiv";
 
-       return alg->cra_flags & CRYPTO_ALG_ASYNC ?
-              "eseqiv" : skcipher_default_geniv;
+       return "eseqiv";
 }
 
 static int crypto_givcipher_default(struct crypto_alg *alg, u32 type, u32 mask)
@@ -709,17 +704,3 @@ err:
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(crypto_alloc_ablkcipher);
-
-static int __init skcipher_module_init(void)
-{
-       skcipher_default_geniv = num_possible_cpus() > 1 ?
-                                "eseqiv" : "chainiv";
-       return 0;
-}
-
-static void skcipher_module_exit(void)
-{
-}
-
-module_init(skcipher_module_init);
-module_exit(skcipher_module_exit);
index ac33d5f3077823af1e714039e2bc093763f8c2fb..966f893711b393a3f4a7f22252b0d1cf6eb8dc98 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 0262210cad386bde9b75f45d823c730920a54756..ef5356cd280a54c086e4f8ff964245e38748a391 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 a1c4f0a555832089129eb77be3680aea03856148..6a6dfc062d2a47f04449fbb0e1c3f3852be337dc 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 c0bb3778f1ae06976fbaf07c7e1b075fc0c581e7..666f1962a160f5d547579b918b6229de0232607b 100644 (file)
@@ -230,11 +230,11 @@ remainder:
         */
        if (byte_count < DEFAULT_BLK_SZ) {
 empty_rbuf:
-               for (; ctx->rand_data_valid < DEFAULT_BLK_SZ;
-                       ctx->rand_data_valid++) {
+               while (ctx->rand_data_valid < DEFAULT_BLK_SZ) {
                        *ptr = ctx->rand_data[ctx->rand_data_valid];
                        ptr++;
                        byte_count--;
+                       ctx->rand_data_valid++;
                        if (byte_count == 0)
                                goto done;
                }
index 6d2c2ea12559c57624b687a06e0b6f3f3ae4291a..03a6eb95ab500bd37fe2b2e4000a73b1c01a99b0 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 cf807654d221c80fced3280ae23bc666a2089348..b77eb53047882ad26de8de4892e8568a4b792326 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 cb2e29180a87286321f593649365c2cb2fc95247..97eb001960b97774e89643f65711d2a68e6a3a8f 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 5e5e35626899e845bb673530854f9aa127336fd6..5c37a22a0637acdc7abf8f07c610c2f72dfd59bb 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 4a6a0696f8a3b165618fe7c62516a7446e354c24..459cf97a75e2e223798e2cd157a60bf048f01117 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <crypto/algapi.h>
 #include "public_key.h"
 
 MODULE_LICENSE("GPL");
@@ -73,13 +74,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 _
 };
 
@@ -189,12 +190,12 @@ static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
                }
        }
 
-       if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) {
+       if (crypto_memneq(asn1_template, EM + T_offset, asn1_size) != 0) {
                kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]");
                return -EBADMSG;
        }
 
-       if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) {
+       if (crypto_memneq(H, EM + T_offset + asn1_size, hash_size) != 0) {
                kleave(" = -EKEYREJECTED [EM[T] hash mismatch]");
                return -EKEYREJECTED;
        }
index facbf26bc6bbbc91eb879b0a5cdf40d01e8f2b05..29893162497ca352101b4d2126a0d03590e50ed1 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 f86dc5fcc4ad46accfb003b464c784fb5763218b..87d9cc26f630625d7c57e3309456de2a356a46b5 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 06007f0e880c330903b5536e9d9c194da302738c..382ef0d2ff2e5e030c0068d1076155364d48f43d 100644 (file)
 #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
-};
-
 /*
- * 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);
-
-error_mpi:
-       mpi_free(sig->rsa.s);
-error:
-       kfree(sig);
-error_no_sig:
-       crypto_free_shash(tfm);
-
-       pr_devel("<==%s() = %d\n", __func__, ret);
        return ret;
 }
+EXPORT_SYMBOL_GPL(x509_check_signature);
 
 /*
  * Attempt to parse a data blob for a key as an X509 certificate.
@@ -106,7 +108,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 +118,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,58 +139,22 @@ 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) {
+       /* 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);
                if (ret < 0)
                        goto error_free_cert;
@@ -237,3 +213,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 9e62feffb374536995e08357866df9787635b19e..f8c0b8dbeb7582beca1ee7fd5c7aaac58aba23cd 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 91d5d385899ee06c507cee1aa496330405d6276b..d05327caf69dbc18532478b122ea9834e67f1fd9 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 a9f08a6a582ebccce298f718d0bbf5db8b1e1a7a..934a849814958e6ea37b9dbdb96abc820c4fe9e1 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 7be34248b450896cfc056d1709513df4f172f79a..39ea4791a3c977ad7eda47498b9f7d7741b438e2 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 8ade0a0481c67149e72d7878deb306a0fd33e5c2..3c562f5a60bbb34f19e6b90f4d858b04d3d496e2 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 4a92bac744dce500a209f5e4e6033d465eff5dc9..dad95f45b88f6566afc62df645151d3a2ae60092 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");
index ffce19de05cf958853dc0e13f7e3cf1efa30cd4f..1875e7026e8f7fad661e2051cdc139f733d6c19a 100644 (file)
@@ -52,40 +52,52 @@ static void authenc_request_complete(struct aead_request *req, int err)
                aead_request_complete(req, err);
 }
 
-static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
-                                unsigned int keylen)
+int crypto_authenc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
+                              unsigned int keylen)
 {
-       unsigned int authkeylen;
-       unsigned int enckeylen;
-       struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
-       struct crypto_ahash *auth = ctx->auth;
-       struct crypto_ablkcipher *enc = ctx->enc;
-       struct rtattr *rta = (void *)key;
+       struct rtattr *rta = (struct rtattr *)key;
        struct crypto_authenc_key_param *param;
-       int err = -EINVAL;
 
        if (!RTA_OK(rta, keylen))
-               goto badkey;
+               return -EINVAL;
        if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
-               goto badkey;
+               return -EINVAL;
        if (RTA_PAYLOAD(rta) < sizeof(*param))
-               goto badkey;
+               return -EINVAL;
 
        param = RTA_DATA(rta);
-       enckeylen = be32_to_cpu(param->enckeylen);
+       keys->enckeylen = be32_to_cpu(param->enckeylen);
 
        key += RTA_ALIGN(rta->rta_len);
        keylen -= RTA_ALIGN(rta->rta_len);
 
-       if (keylen < enckeylen)
-               goto badkey;
+       if (keylen < keys->enckeylen)
+               return -EINVAL;
 
-       authkeylen = keylen - enckeylen;
+       keys->authkeylen = keylen - keys->enckeylen;
+       keys->authkey = key;
+       keys->enckey = key + keys->authkeylen;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_authenc_extractkeys);
+
+static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
+                                unsigned int keylen)
+{
+       struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
+       struct crypto_ahash *auth = ctx->auth;
+       struct crypto_ablkcipher *enc = ctx->enc;
+       struct crypto_authenc_keys keys;
+       int err = -EINVAL;
+
+       if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
+               goto badkey;
 
        crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
        crypto_ahash_set_flags(auth, crypto_aead_get_flags(authenc) &
                                    CRYPTO_TFM_REQ_MASK);
-       err = crypto_ahash_setkey(auth, keyauthkeylen);
+       err = crypto_ahash_setkey(auth, keys.authkey, keys.authkeylen);
        crypto_aead_set_flags(authenc, crypto_ahash_get_flags(auth) &
                                       CRYPTO_TFM_RES_MASK);
 
@@ -95,7 +107,7 @@ static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
        crypto_ablkcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
        crypto_ablkcipher_set_flags(enc, crypto_aead_get_flags(authenc) &
                                         CRYPTO_TFM_REQ_MASK);
-       err = crypto_ablkcipher_setkey(enc, key + authkeylen, enckeylen);
+       err = crypto_ablkcipher_setkey(enc, keys.enckey, keys.enckeylen);
        crypto_aead_set_flags(authenc, crypto_ablkcipher_get_flags(enc) &
                                       CRYPTO_TFM_RES_MASK);
 
@@ -188,7 +200,7 @@ static void authenc_verify_ahash_update_done(struct crypto_async_request *areq,
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
 
-       err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
+       err = crypto_memneq(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
        if (err)
                goto out;
 
@@ -227,7 +239,7 @@ static void authenc_verify_ahash_done(struct crypto_async_request *areq,
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
 
-       err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
+       err = crypto_memneq(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
        if (err)
                goto out;
 
@@ -462,7 +474,7 @@ static int crypto_authenc_verify(struct aead_request *req,
        ihash = ohash + authsize;
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
-       return memcmp(ihash, ohash, authsize) ? -EBADMSG : 0;
+       return crypto_memneq(ihash, ohash, authsize) ? -EBADMSG : 0;
 }
 
 static int crypto_authenc_iverify(struct aead_request *req, u8 *iv,
index ab53762fc309c5db55ccaab948f1862bcc7a237a..4be0dd4373a9a2e8a15b62adbf8dbdd6e374eacd 100644 (file)
@@ -59,37 +59,19 @@ static void authenc_esn_request_complete(struct aead_request *req, int err)
 static int crypto_authenc_esn_setkey(struct crypto_aead *authenc_esn, const u8 *key,
                                     unsigned int keylen)
 {
-       unsigned int authkeylen;
-       unsigned int enckeylen;
        struct crypto_authenc_esn_ctx *ctx = crypto_aead_ctx(authenc_esn);
        struct crypto_ahash *auth = ctx->auth;
        struct crypto_ablkcipher *enc = ctx->enc;
-       struct rtattr *rta = (void *)key;
-       struct crypto_authenc_key_param *param;
+       struct crypto_authenc_keys keys;
        int err = -EINVAL;
 
-       if (!RTA_OK(rta, keylen))
+       if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
                goto badkey;
-       if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
-               goto badkey;
-       if (RTA_PAYLOAD(rta) < sizeof(*param))
-               goto badkey;
-
-       param = RTA_DATA(rta);
-       enckeylen = be32_to_cpu(param->enckeylen);
-
-       key += RTA_ALIGN(rta->rta_len);
-       keylen -= RTA_ALIGN(rta->rta_len);
-
-       if (keylen < enckeylen)
-               goto badkey;
-
-       authkeylen = keylen - enckeylen;
 
        crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
        crypto_ahash_set_flags(auth, crypto_aead_get_flags(authenc_esn) &
                                     CRYPTO_TFM_REQ_MASK);
-       err = crypto_ahash_setkey(auth, keyauthkeylen);
+       err = crypto_ahash_setkey(auth, keys.authkey, keys.authkeylen);
        crypto_aead_set_flags(authenc_esn, crypto_ahash_get_flags(auth) &
                                           CRYPTO_TFM_RES_MASK);
 
@@ -99,7 +81,7 @@ static int crypto_authenc_esn_setkey(struct crypto_aead *authenc_esn, const u8 *
        crypto_ablkcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
        crypto_ablkcipher_set_flags(enc, crypto_aead_get_flags(authenc_esn) &
                                         CRYPTO_TFM_REQ_MASK);
-       err = crypto_ablkcipher_setkey(enc, key + authkeylen, enckeylen);
+       err = crypto_ablkcipher_setkey(enc, keys.enckey, keys.enckeylen);
        crypto_aead_set_flags(authenc_esn, crypto_ablkcipher_get_flags(enc) &
                                           CRYPTO_TFM_RES_MASK);
 
@@ -247,7 +229,7 @@ static void authenc_esn_verify_ahash_update_done(struct crypto_async_request *ar
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
 
-       err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
+       err = crypto_memneq(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
        if (err)
                goto out;
 
@@ -296,7 +278,7 @@ static void authenc_esn_verify_ahash_update_done2(struct crypto_async_request *a
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
 
-       err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
+       err = crypto_memneq(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
        if (err)
                goto out;
 
@@ -336,7 +318,7 @@ static void authenc_esn_verify_ahash_done(struct crypto_async_request *areq,
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
 
-       err = memcmp(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
+       err = crypto_memneq(ihash, ahreq->result, authsize) ? -EBADMSG : 0;
        if (err)
                goto out;
 
@@ -568,7 +550,7 @@ static int crypto_authenc_esn_verify(struct aead_request *req)
        ihash = ohash + authsize;
        scatterwalk_map_and_copy(ihash, areq_ctx->sg, areq_ctx->cryptlen,
                                 authsize, 0);
-       return memcmp(ihash, ohash, authsize) ? -EBADMSG : 0;
+       return crypto_memneq(ihash, ohash, authsize) ? -EBADMSG : 0;
 }
 
 static int crypto_authenc_esn_iverify(struct aead_request *req, u8 *iv,
index 499c91717d937bfaba9364a5bcde94988626195c..3e05499d183aa8e49d41b83be448e7f52d76b10c 100644 (file)
@@ -363,7 +363,7 @@ static void crypto_ccm_decrypt_done(struct crypto_async_request *areq,
 
        if (!err) {
                err = crypto_ccm_auth(req, req->dst, cryptlen);
-               if (!err && memcmp(pctx->auth_tag, pctx->odata, authsize))
+               if (!err && crypto_memneq(pctx->auth_tag, pctx->odata, authsize))
                        err = -EBADMSG;
        }
        aead_request_complete(req, err);
@@ -422,7 +422,7 @@ static int crypto_ccm_decrypt(struct aead_request *req)
                return err;
 
        /* verify */
-       if (memcmp(authtag, odata, authsize))
+       if (crypto_memneq(authtag, odata, authsize))
                return -EBADMSG;
 
        return err;
index 43e1fb05ea54878cbe136231a1a92c847b6d3119..b4f01793900409a0398faeb958b74416a5d1637d 100644 (file)
@@ -582,7 +582,7 @@ static int crypto_gcm_verify(struct aead_request *req,
 
        crypto_xor(auth_tag, iauth_tag, 16);
        scatterwalk_map_and_copy(iauth_tag, req->src, cryptlen, authsize, 0);
-       return memcmp(iauth_tag, auth_tag, authsize) ? -EBADMSG : 0;
+       return crypto_memneq(iauth_tag, auth_tag, authsize) ? -EBADMSG : 0;
 }
 
 static void gcm_decrypt_done(struct crypto_async_request *areq, int err)
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);
diff --git a/crypto/memneq.c b/crypto/memneq.c
new file mode 100644 (file)
index 0000000..cd01622
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Constant-time equality testing of memory regions.
+ *
+ * Authors:
+ *
+ *   James Yonan <james@openvpn.net>
+ *   Daniel Borkmann <dborkman@redhat.com>
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 OpenVPN Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it 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.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 OpenVPN Technologies, Inc. All rights reserved.
+ *
+ * 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 OpenVPN Technologies nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <crypto/algapi.h>
+
+#ifndef __HAVE_ARCH_CRYPTO_MEMNEQ
+
+/* Generic path for arbitrary size */
+static inline unsigned long
+__crypto_memneq_generic(const void *a, const void *b, size_t size)
+{
+       unsigned long neq = 0;
+
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+       while (size >= sizeof(unsigned long)) {
+               neq |= *(unsigned long *)a ^ *(unsigned long *)b;
+               a += sizeof(unsigned long);
+               b += sizeof(unsigned long);
+               size -= sizeof(unsigned long);
+       }
+#endif /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */
+       while (size > 0) {
+               neq |= *(unsigned char *)a ^ *(unsigned char *)b;
+               a += 1;
+               b += 1;
+               size -= 1;
+       }
+       return neq;
+}
+
+/* Loop-free fast-path for frequently used 16-byte size */
+static inline unsigned long __crypto_memneq_16(const void *a, const void *b)
+{
+#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+       if (sizeof(unsigned long) == 8)
+               return ((*(unsigned long *)(a)   ^ *(unsigned long *)(b))
+                     | (*(unsigned long *)(a+8) ^ *(unsigned long *)(b+8)));
+       else if (sizeof(unsigned int) == 4)
+               return ((*(unsigned int *)(a)    ^ *(unsigned int *)(b))
+                      | (*(unsigned int *)(a+4)  ^ *(unsigned int *)(b+4))
+                     | (*(unsigned int *)(a+8)  ^ *(unsigned int *)(b+8))
+                     | (*(unsigned int *)(a+12) ^ *(unsigned int *)(b+12)));
+       else
+#endif /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */
+               return ((*(unsigned char *)(a)    ^ *(unsigned char *)(b))
+                     | (*(unsigned char *)(a+1)  ^ *(unsigned char *)(b+1))
+                     | (*(unsigned char *)(a+2)  ^ *(unsigned char *)(b+2))
+                     | (*(unsigned char *)(a+3)  ^ *(unsigned char *)(b+3))
+                     | (*(unsigned char *)(a+4)  ^ *(unsigned char *)(b+4))
+                     | (*(unsigned char *)(a+5)  ^ *(unsigned char *)(b+5))
+                     | (*(unsigned char *)(a+6)  ^ *(unsigned char *)(b+6))
+                     | (*(unsigned char *)(a+7)  ^ *(unsigned char *)(b+7))
+                     | (*(unsigned char *)(a+8)  ^ *(unsigned char *)(b+8))
+                     | (*(unsigned char *)(a+9)  ^ *(unsigned char *)(b+9))
+                     | (*(unsigned char *)(a+10) ^ *(unsigned char *)(b+10))
+                     | (*(unsigned char *)(a+11) ^ *(unsigned char *)(b+11))
+                     | (*(unsigned char *)(a+12) ^ *(unsigned char *)(b+12))
+                     | (*(unsigned char *)(a+13) ^ *(unsigned char *)(b+13))
+                     | (*(unsigned char *)(a+14) ^ *(unsigned char *)(b+14))
+                     | (*(unsigned char *)(a+15) ^ *(unsigned char *)(b+15)));
+}
+
+/* Compare two areas of memory without leaking timing information,
+ * and with special optimizations for common sizes.  Users should
+ * not call this function directly, but should instead use
+ * crypto_memneq defined in crypto/algapi.h.
+ */
+noinline unsigned long __crypto_memneq(const void *a, const void *b,
+                                      size_t size)
+{
+       switch (size) {
+       case 16:
+               return __crypto_memneq_16(a, b);
+       default:
+               return __crypto_memneq_generic(a, b, size);
+       }
+}
+EXPORT_SYMBOL(__crypto_memneq);
+
+#endif /* __HAVE_ARCH_CRYPTO_MEMNEQ */
index 25a5934f0e50a011f73db08a84735ae76b2a7f60..1ab8258fcf560735237a198b2037c102e229d20b 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 e091ef6e17912b24020bf0a1668f33602b770cf9..432afc03e7c3f9c1040ac54033950eb9e17c0727 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 c95df0b8c8808e505f9dbfc7c8bcc68da48af57d..5d9248526d780b06c51bee967cdd088c9c90e5e5 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 b9f0d5f4bba51cecb6ffe8fda126762850818263..8711e3797165fa73fd0c401cedbb54c61eb68e4a 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 d3961014aad7ff9d77bcb296ec2f851f96d64b5b..6745fe137b9ea541ae729429035eaeca8fc8fc07 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 8a4cfc7e71f0f83cc7a8644e75eab627c8e3c52d..dbfe49e5fd63cc179559b2c5caee57d27324c012 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 f691d0e4d9fa091faeb0ebc231e8701b399c264b..ff97430455cbe923d3bad4f453635371b9388867 100644 (file)
@@ -184,7 +184,7 @@ acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer,
                             struct acpi_buffer *output_buffer);
 
 acpi_status
-acpi_rs_create_aml_resources(struct acpi_resource *linked_list_buffer,
+acpi_rs_create_aml_resources(struct acpi_buffer *resource_list,
                             struct acpi_buffer *output_buffer);
 
 acpi_status
@@ -227,8 +227,8 @@ acpi_rs_get_list_length(u8 * aml_buffer,
                        u32 aml_buffer_length, acpi_size * size_needed);
 
 acpi_status
-acpi_rs_get_aml_length(struct acpi_resource *linked_list_buffer,
-                      acpi_size * size_needed);
+acpi_rs_get_aml_length(struct acpi_resource *resource_list,
+                      acpi_size resource_list_size, acpi_size * size_needed);
 
 acpi_status
 acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object,
index 243737363fb8ea51c80f77a16908659a29271a48..fd1ff54cda1911a1e520440afd8a48125d927069 100644 (file)
@@ -106,6 +106,7 @@ struct acpi_namespace_node *acpi_ns_create_node(u32 name)
 void acpi_ns_delete_node(struct acpi_namespace_node *node)
 {
        union acpi_operand_object *obj_desc;
+       union acpi_operand_object *next_desc;
 
        ACPI_FUNCTION_NAME(ns_delete_node);
 
@@ -114,12 +115,13 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node)
        acpi_ns_detach_object(node);
 
        /*
-        * Delete an attached data object if present (an object that was created
-        * and attached via acpi_attach_data). Note: After any normal object is
-        * detached above, the only possible remaining object is a data object.
+        * Delete an attached data object list if present (objects that were
+        * attached via acpi_attach_data). Note: After any normal object is
+        * detached above, the only possible remaining object(s) are data
+        * objects, in a linked list.
         */
        obj_desc = node->object;
-       if (obj_desc && (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) {
+       while (obj_desc && (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) {
 
                /* Invoke the attached data deletion handler if present */
 
@@ -127,7 +129,15 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node)
                        obj_desc->data.handler(node, obj_desc->data.pointer);
                }
 
+               next_desc = obj_desc->common.next_object;
                acpi_ut_remove_reference(obj_desc);
+               obj_desc = next_desc;
+       }
+
+       /* Special case for the statically allocated root node */
+
+       if (node == acpi_gbl_root_node) {
+               return;
        }
 
        /* Now we can delete the node */
index cc2fea94c5f0939fcbfe87c676c0e869171a14f5..4a0665b6bcc11c6c6eb8aad3ee62be15b6122694 100644 (file)
@@ -593,24 +593,26 @@ struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle)
 
 void acpi_ns_terminate(void)
 {
-       union acpi_operand_object *obj_desc;
+       acpi_status status;
 
        ACPI_FUNCTION_TRACE(ns_terminate);
 
        /*
-        * 1) Free the entire namespace -- all nodes and objects
-        *
-        * Delete all object descriptors attached to namepsace nodes
+        * Free the entire namespace -- all nodes and all objects
+        * attached to the nodes
         */
        acpi_ns_delete_namespace_subtree(acpi_gbl_root_node);
 
-       /* Detach any objects attached to the root */
+       /* Delete any objects attached to the root node */
 
-       obj_desc = acpi_ns_get_attached_object(acpi_gbl_root_node);
-       if (obj_desc) {
-               acpi_ns_detach_object(acpi_gbl_root_node);
+       status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+       if (ACPI_FAILURE(status)) {
+               return_VOID;
        }
 
+       acpi_ns_delete_node(acpi_gbl_root_node);
+       (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Namespace freed\n"));
        return_VOID;
 }
index b62a0f4f4f9bb527cd49fc95279b7032ba1a689a..b60c9cf82862f9e94dfb254b6a30b003e2dd5426 100644 (file)
@@ -174,6 +174,7 @@ acpi_rs_stream_option_length(u32 resource_length,
  * FUNCTION:    acpi_rs_get_aml_length
  *
  * PARAMETERS:  resource            - Pointer to the resource linked list
+ *              resource_list_size  - Size of the resource linked list
  *              size_needed         - Where the required size is returned
  *
  * RETURN:      Status
@@ -185,16 +186,20 @@ acpi_rs_stream_option_length(u32 resource_length,
  ******************************************************************************/
 
 acpi_status
-acpi_rs_get_aml_length(struct acpi_resource * resource, acpi_size * size_needed)
+acpi_rs_get_aml_length(struct acpi_resource *resource,
+                      acpi_size resource_list_size, acpi_size * size_needed)
 {
        acpi_size aml_size_needed = 0;
+       struct acpi_resource *resource_end;
        acpi_rs_length total_size;
 
        ACPI_FUNCTION_TRACE(rs_get_aml_length);
 
        /* Traverse entire list of internal resource descriptors */
 
-       while (resource) {
+       resource_end =
+           ACPI_ADD_PTR(struct acpi_resource, resource, resource_list_size);
+       while (resource < resource_end) {
 
                /* Validate the descriptor type */
 
index 65f3e1c5b5989f61bdcb03e6620cdd1a7371dba9..3a2ace93e62cf5d11690a4a4a907ae5539007f9e 100644 (file)
@@ -418,22 +418,21 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
  *
  * FUNCTION:    acpi_rs_create_aml_resources
  *
- * PARAMETERS:  linked_list_buffer      - Pointer to the resource linked list
- *              output_buffer           - Pointer to the user's buffer
+ * PARAMETERS:  resource_list           - Pointer to the resource list buffer
+ *              output_buffer           - Where the AML buffer is returned
  *
  * RETURN:      Status  AE_OK if okay, else a valid acpi_status code.
  *              If the output_buffer is too small, the error will be
  *              AE_BUFFER_OVERFLOW and output_buffer->Length will point
  *              to the size buffer needed.
  *
- * DESCRIPTION: Takes the linked list of device resources and
- *              creates a bytestream to be used as input for the
- *              _SRS control method.
+ * DESCRIPTION: Converts a list of device resources to an AML bytestream
+ *              to be used as input for the _SRS control method.
  *
  ******************************************************************************/
 
 acpi_status
-acpi_rs_create_aml_resources(struct acpi_resource *linked_list_buffer,
+acpi_rs_create_aml_resources(struct acpi_buffer *resource_list,
                             struct acpi_buffer *output_buffer)
 {
        acpi_status status;
@@ -441,16 +440,16 @@ acpi_rs_create_aml_resources(struct acpi_resource *linked_list_buffer,
 
        ACPI_FUNCTION_TRACE(rs_create_aml_resources);
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "LinkedListBuffer = %p\n",
-                         linked_list_buffer));
+       /* Params already validated, no need to re-validate here */
 
-       /*
-        * Params already validated, so we don't re-validate here
-        *
-        * Pass the linked_list_buffer into a module that calculates
-        * the buffer size needed for the byte stream.
-        */
-       status = acpi_rs_get_aml_length(linked_list_buffer, &aml_size_needed);
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ResourceList Buffer = %p\n",
+                         resource_list->pointer));
+
+       /* Get the buffer size needed for the AML byte stream */
+
+       status = acpi_rs_get_aml_length(resource_list->pointer,
+                                       resource_list->length,
+                                       &aml_size_needed);
 
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "AmlSizeNeeded=%X, %s\n",
                          (u32)aml_size_needed, acpi_format_exception(status)));
@@ -467,10 +466,9 @@ acpi_rs_create_aml_resources(struct acpi_resource *linked_list_buffer,
 
        /* Do the conversion */
 
-       status =
-           acpi_rs_convert_resources_to_aml(linked_list_buffer,
-                                            aml_size_needed,
-                                            output_buffer->pointer);
+       status = acpi_rs_convert_resources_to_aml(resource_list->pointer,
+                                                 aml_size_needed,
+                                                 output_buffer->pointer);
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
        }
index aef303d56d86fecc84e397cad3c24280a874dbb4..14a7982c9961088ae50c0283bfefb4b4b5ccb488 100644 (file)
@@ -753,7 +753,7 @@ acpi_rs_set_srs_method_data(struct acpi_namespace_node *node,
         * Convert the linked list into a byte stream
         */
        buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER;
-       status = acpi_rs_create_aml_resources(in_buffer->pointer, &buffer);
+       status = acpi_rs_create_aml_resources(in_buffer, &buffer);
        if (ACPI_FAILURE(status)) {
                goto cleanup;
        }
index 1a67b3944b3b8db426fb3edf7b878153785b7631..03ae8affe48f097b1aae7346edd25a165f23186a 100644 (file)
@@ -185,6 +185,7 @@ acpi_debug_print(u32 requested_debug_level,
                }
 
                acpi_gbl_prev_thread_id = thread_id;
+               acpi_gbl_nesting_level = 0;
        }
 
        /*
@@ -193,13 +194,21 @@ acpi_debug_print(u32 requested_debug_level,
         */
        acpi_os_printf("%9s-%04ld ", module_name, line_number);
 
+#ifdef ACPI_EXEC_APP
+       /*
+        * For acpi_exec only, emit the thread ID and nesting level.
+        * Note: nesting level is really only useful during a single-thread
+        * execution. Otherwise, multiple threads will keep resetting the
+        * level.
+        */
        if (ACPI_LV_THREADS & acpi_dbg_level) {
                acpi_os_printf("[%u] ", (u32)thread_id);
        }
 
-       acpi_os_printf("[%02ld] %-22.22s: ",
-                      acpi_gbl_nesting_level,
-                      acpi_ut_trim_function_name(function_name));
+       acpi_os_printf("[%02ld] ", acpi_gbl_nesting_level);
+#endif
+
+       acpi_os_printf("%-22.22s: ", acpi_ut_trim_function_name(function_name));
 
        va_start(args, format);
        acpi_os_vprintf(format, args);
@@ -420,7 +429,9 @@ acpi_ut_exit(u32 line_number,
                                 component_id, "%s\n", acpi_gbl_fn_exit_str);
        }
 
-       acpi_gbl_nesting_level--;
+       if (acpi_gbl_nesting_level) {
+               acpi_gbl_nesting_level--;
+       }
 }
 
 ACPI_EXPORT_SYMBOL(acpi_ut_exit)
@@ -467,7 +478,9 @@ acpi_ut_status_exit(u32 line_number,
                }
        }
 
-       acpi_gbl_nesting_level--;
+       if (acpi_gbl_nesting_level) {
+               acpi_gbl_nesting_level--;
+       }
 }
 
 ACPI_EXPORT_SYMBOL(acpi_ut_status_exit)
@@ -504,7 +517,9 @@ acpi_ut_value_exit(u32 line_number,
                                 ACPI_FORMAT_UINT64(value));
        }
 
-       acpi_gbl_nesting_level--;
+       if (acpi_gbl_nesting_level) {
+               acpi_gbl_nesting_level--;
+       }
 }
 
 ACPI_EXPORT_SYMBOL(acpi_ut_value_exit)
@@ -540,7 +555,9 @@ acpi_ut_ptr_exit(u32 line_number,
                                 ptr);
        }
 
-       acpi_gbl_nesting_level--;
+       if (acpi_gbl_nesting_level) {
+               acpi_gbl_nesting_level--;
+       }
 }
 
 #endif
index 13e045025c3366f00e4912aa1063f538d44f4687..517af700399da1e0c8f16c96baab96e7cad6299b 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 fb848378d5824691af9e84b4f100a01e47df7d39..078c4f7fe2dd97c42d3e13ea11ebde804e3ae4f0 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 d42b2fb5a7e94131ce2633ef95f4d7631729325f..b3480cf7db1a1d1eba0e8d884cb456f92b76b660 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 d5309fd494589b4d1596cb1c03576ed1e7ef252e..ba5b56db9d27c7fafa3b19c6d1f5d2549308aea6 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 fdef416c0ff6f0384e107466b47b1ff8f2bac9aa..cae3b387b867a99ab7013cfc2b731b2125abc961 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 10f0f40587bb73309eee9e959fc1049cbaf6dc05..a22a295edb692347f16066bff26d145973ab2523 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 266bc58ce0ce165eb4c1a6bf707c7af5d5080c36..386a9fe497b451d0da6ebed2bc4b6f1a2dacda71 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <linux/acpi_io.h>
-#include <acpi/acpiosxf.h>
 
 /* ACPI NVS regions, APEI may use it */
 
index 56f05869b08df2ab89bafd661d97b282c36d0b8b..20360e480bd8d81c0070cc25b5cd2cddd05d0447 100644 (file)
@@ -65,6 +65,9 @@ static struct acpi_scan_handler pci_root_handler = {
        .ids = root_device_ids,
        .attach = acpi_pci_root_add,
        .detach = acpi_pci_root_remove,
+       .hotplug = {
+               .ignore = true,
+       },
 };
 
 static DEFINE_MUTEX(osc_lock);
@@ -575,6 +578,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 55f9dedbbf9fedc5aae2cf21852c7474514cd0d1..fd39459926b1564c2faa3d367a4dbbf05aa60031 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);
@@ -1780,7 +1772,7 @@ static void acpi_scan_init_hotplug(acpi_handle handle, int type)
         */
        list_for_each_entry(hwid, &pnp.ids, list) {
                handler = acpi_scan_match_handler(hwid->id, NULL);
-               if (handler) {
+               if (handler && !handler->hotplug.ignore) {
                        acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
                                        acpi_hotplug_notify_cb, handler);
                        break;
@@ -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 14df30580e154802aca352176ada8214f7ea6067..721e949e606edff0e2b62dc1e4d12e201bbbad4d 100644 (file)
@@ -525,7 +525,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
         * generate wakeup events.
         */
        if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
-               acpi_event_status pwr_btn_status;
+               acpi_event_status pwr_btn_status = ACPI_EVENT_FLAG_DISABLED;
 
                acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
 
index db5293650f622108e80a09124097f469c727547f..6dbc3ca45223a1ba30f995f016ecd965dd4861f7 100644 (file)
@@ -309,7 +309,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
                sprintf(table_attr->name + ACPI_NAME_SIZE, "%d",
                        table_attr->instance);
 
-       table_attr->attr.size = 0;
+       table_attr->attr.size = table_header->length;
        table_attr->attr.read = acpi_table_show;
        table_attr->attr.attr.name = table_attr->name;
        table_attr->attr.attr.mode = 0400;
@@ -354,8 +354,9 @@ static int acpi_tables_sysfs_init(void)
 {
        struct acpi_table_attr *table_attr;
        struct acpi_table_header *table_header = NULL;
-       int table_index = 0;
-       int result;
+       int table_index;
+       acpi_status status;
+       int ret;
 
        tables_kobj = kobject_create_and_add("tables", acpi_kobj);
        if (!tables_kobj)
@@ -365,33 +366,34 @@ static int acpi_tables_sysfs_init(void)
        if (!dynamic_tables_kobj)
                goto err_dynamic_tables;
 
-       do {
-               result = acpi_get_table_by_index(table_index, &table_header);
-               if (!result) {
-                       table_index++;
-                       table_attr = NULL;
-                       table_attr =
-                           kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL);
-                       if (!table_attr)
-                               return -ENOMEM;
-
-                       acpi_table_attr_init(table_attr, table_header);
-                       result =
-                           sysfs_create_bin_file(tables_kobj,
-                                                 &table_attr->attr);
-                       if (result) {
-                               kfree(table_attr);
-                               return result;
-                       } else
-                               list_add_tail(&table_attr->node,
-                                             &acpi_table_attr_list);
+       for (table_index = 0;; table_index++) {
+               status = acpi_get_table_by_index(table_index, &table_header);
+
+               if (status == AE_BAD_PARAMETER)
+                       break;
+
+               if (ACPI_FAILURE(status))
+                       continue;
+
+               table_attr = NULL;
+               table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
+               if (!table_attr)
+                       return -ENOMEM;
+
+               acpi_table_attr_init(table_attr, table_header);
+               ret = sysfs_create_bin_file(tables_kobj, &table_attr->attr);
+               if (ret) {
+                       kfree(table_attr);
+                       return ret;
                }
-       } while (!result);
+               list_add_tail(&table_attr->node, &acpi_table_attr_list);
+       }
+
        kobject_uevent(tables_kobj, KOBJ_ADD);
        kobject_uevent(dynamic_tables_kobj, KOBJ_ADD);
-       result = acpi_install_table_handler(acpi_sysfs_table_handler, NULL);
+       status = acpi_install_table_handler(acpi_sysfs_table_handler, NULL);
 
-       return result == AE_OK ? 0 : -EINVAL;
+       return ACPI_FAILURE(status) ? -EINVAL : 0;
 err_dynamic_tables:
        kobject_put(tables_kobj);
 err:
index 18dbdff4656e7d6c6b8f665e9fcdb2e22067cf98..995e91bcb97b7b4d5f21d585de1f5a7e46bd9ebd 100644 (file)
@@ -81,13 +81,6 @@ module_param(brightness_switch_enabled, bool, 0644);
 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 e2903d03180e1190410c3054ee78990a007e93b2..14f1e95063380a5e2a315428fc8a4d5e8bcc39d3 100644 (file)
@@ -435,6 +435,8 @@ static const struct pci_device_id ahci_pci_tbl[] = {
          .driver_data = board_ahci_yes_fbs },                  /* 88se9172 on some Gigabyte */
        { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3),
          .driver_data = board_ahci_yes_fbs },
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230),
+         .driver_data = board_ahci_yes_fbs },
 
        /* Promise */
        { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci },   /* PDC42819 */
index f9554318504f8a401c97b2221831b1cdbdcdb67f..4b231baceb0995557c2cae40e501f6d3449dc365 100644 (file)
@@ -329,6 +329,7 @@ static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
 static const struct of_device_id ahci_of_match[] = {
        { .compatible = "snps,spear-ahci", },
        { .compatible = "snps,exynos5440-ahci", },
+       { .compatible = "ibm,476gtr-ahci", },
        {},
 };
 MODULE_DEVICE_TABLE(of, ahci_of_match);
index ab714d2ad978644752ca3c9bdff74ae1c9f63934..4372cfa883c9c36cf2624f88186ef3e0e7b27d31 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 81a94a3919dbb0992c079e84130a1c850d81e587..75b93678bbcd7f4231e0c2028995795487db671c 100644 (file)
@@ -6304,10 +6304,9 @@ static void ata_port_detach(struct ata_port *ap)
                for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
                        ata_tlink_delete(&ap->pmp_link[i]);
        }
-       ata_tport_delete(ap);
-
        /* remove the associated SCSI host */
        scsi_remove_host(ap->scsi_host);
+       ata_tport_delete(ap);
 }
 
 /**
index 77bbc8266883134fdfacb40d58fc991630499af8..92d7797223be12c41fb8cbcef92035781a779111 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 68f9e3293e9c62971486937d6a5048c58b1a056c..88949c6d55ddd43b32b07accdb892256028d5e25 100644 (file)
@@ -88,15 +88,13 @@ static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
 static bool odd_can_poweroff(struct ata_device *ata_dev)
 {
        acpi_handle handle;
-       acpi_status status;
        struct acpi_device *acpi_dev;
 
        handle = ata_dev_acpi_handle(ata_dev);
        if (!handle)
                return false;
 
-       status = acpi_bus_get_device(handle, &acpi_dev);
-       if (ACPI_FAILURE(status))
+       if (acpi_bus_get_device(handle, &acpi_dev))
                return false;
 
        return acpi_device_can_poweroff(acpi_dev);
index 853f610af28fbc9dff0ee59d1fa1e6688cbfd732..73492dd4a4bce8aade1f79a7dbdfd3926c7f6f6b 100644 (file)
@@ -319,6 +319,7 @@ static int cf_init(struct arasan_cf_dev *acdev)
        ret = clk_set_rate(acdev->clk, 166000000);
        if (ret) {
                dev_warn(acdev->host->dev, "clock set rate failed");
+               clk_disable_unprepare(acdev->clk);
                return ret;
        }
 
@@ -396,8 +397,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 272f00927761b1e34e5676ad018e0f72ef623b8c..1bdf104e90bb7f1924acf51f78caa39817d78efb 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 99802d6f3c60f603efcff5d342ba0c9f4c937ccd..165c2c299e579805a1f234aff9d6ccf6d0c1666c 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 47051cd251132ce97752bc8f8f9748be272c0f35..3a94b799f16640eb6a8e34ef2df78a31e3085e9c 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 ee039afe90786510eb19e0a536beb30413b27a38..1b41fca3d65a54545c6c124e0df696998c29a1af 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 4ff85b8785ee180a5637f56a43ab87f61dcbe2c6..748dea4f34dc1e18b089557478a2b00221bd83c5 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 0c004ac05811de58b3a0cf49e7a3558010de7187..b35fc4f5237c3b44c7c51aaa0517e58a42a2ab81 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 b5d842370cc9e75bfcf67f38ca6c03ddb259e8fe..ea192ec029c45bf7354d8184b44e1eb460cdbf77 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 5cdf88b7ad9e72a36bc9aa25755b03485c987418..6a680d4de7f1c3dcfa7999e45efc98b4b14d99fc 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 c206de2951f20f086c099ab69e8a1a48286465be..2f2b08457c673547568187f04863a01947163385 100644 (file)
@@ -165,6 +165,19 @@ config HW_RANDOM_OMAP
 
          If unsure, say Y.
 
+config HW_RANDOM_OMAP3_ROM
+       tristate "OMAP3 ROM Random Number Generator support"
+       depends on HW_RANDOM && ARCH_OMAP3
+       default HW_RANDOM
+       ---help---
+         This driver provides kernel-side support for the Random Number
+         Generator hardware found on OMAP34xx processors.
+
+         To compile this driver as a module, choose M here: the
+         module will be called omap3-rom-rng.
+
+         If unsure, say Y.
+
 config HW_RANDOM_OCTEON
        tristate "Octeon Random Number Generator support"
        depends on HW_RANDOM && CAVIUM_OCTEON_SOC
@@ -327,3 +340,15 @@ config HW_RANDOM_TPM
          module will be called tpm-rng.
 
          If unsure, say Y.
+
+config HW_RANDOM_MSM
+       tristate "Qualcomm MSM Random Number Generator support"
+       depends on HW_RANDOM && ARCH_MSM
+       ---help---
+         This driver provides kernel-side support for the Random Number
+         Generator hardware found on Qualcomm MSM SoCs.
+
+         To compile this driver as a module, choose M here. the
+         module will be called msm-rng.
+
+         If unsure, say Y.
index d7d2435ff7fa8d38cd129d327c59ded717e3682a..3ae7755a52e706bd8356e8ae5bc660fe8a693767 100644 (file)
@@ -15,6 +15,7 @@ n2-rng-y := n2-drv.o n2-asm.o
 obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o
 obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
 obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
+obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o
 obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o
 obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
 obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o
@@ -28,3 +29,4 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
 obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
 obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
 obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o
diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c
new file mode 100644 (file)
index 0000000..148521e
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. 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 and
+ * only 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.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT          0x0000
+#define PRNG_STATUS            0x0004
+#define PRNG_LFSR_CFG          0x0100
+#define PRNG_CONFIG            0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK     0x0000ffff
+#define PRNG_LFSR_CFG_CLOCKS   0x0000dddd
+#define PRNG_CONFIG_HW_ENABLE  BIT(1)
+#define PRNG_STATUS_DATA_AVAIL BIT(0)
+
+#define MAX_HW_FIFO_DEPTH      16
+#define MAX_HW_FIFO_SIZE       (MAX_HW_FIFO_DEPTH * 4)
+#define WORD_SZ                        4
+
+struct msm_rng {
+       void __iomem *base;
+       struct clk *clk;
+       struct hwrng hwrng;
+};
+
+#define to_msm_rng(p)  container_of(p, struct msm_rng, hwrng)
+
+static int msm_rng_enable(struct hwrng *hwrng, int enable)
+{
+       struct msm_rng *rng = to_msm_rng(hwrng);
+       u32 val;
+       int ret;
+
+       ret = clk_prepare_enable(rng->clk);
+       if (ret)
+               return ret;
+
+       if (enable) {
+               /* Enable PRNG only if it is not already enabled */
+               val = readl_relaxed(rng->base + PRNG_CONFIG);
+               if (val & PRNG_CONFIG_HW_ENABLE)
+                       goto already_enabled;
+
+               val = readl_relaxed(rng->base + PRNG_LFSR_CFG);
+               val &= ~PRNG_LFSR_CFG_MASK;
+               val |= PRNG_LFSR_CFG_CLOCKS;
+               writel(val, rng->base + PRNG_LFSR_CFG);
+
+               val = readl_relaxed(rng->base + PRNG_CONFIG);
+               val |= PRNG_CONFIG_HW_ENABLE;
+               writel(val, rng->base + PRNG_CONFIG);
+       } else {
+               val = readl_relaxed(rng->base + PRNG_CONFIG);
+               val &= ~PRNG_CONFIG_HW_ENABLE;
+               writel(val, rng->base + PRNG_CONFIG);
+       }
+
+already_enabled:
+       clk_disable_unprepare(rng->clk);
+       return 0;
+}
+
+static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait)
+{
+       struct msm_rng *rng = to_msm_rng(hwrng);
+       size_t currsize = 0;
+       u32 *retdata = data;
+       size_t maxsize;
+       int ret;
+       u32 val;
+
+       /* calculate max size bytes to transfer back to caller */
+       maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
+
+       /* no room for word data */
+       if (maxsize < WORD_SZ)
+               return 0;
+
+       ret = clk_prepare_enable(rng->clk);
+       if (ret)
+               return ret;
+
+       /* read random data from hardware */
+       do {
+               val = readl_relaxed(rng->base + PRNG_STATUS);
+               if (!(val & PRNG_STATUS_DATA_AVAIL))
+                       break;
+
+               val = readl_relaxed(rng->base + PRNG_DATA_OUT);
+               if (!val)
+                       break;
+
+               *retdata++ = val;
+               currsize += WORD_SZ;
+
+               /* make sure we stay on 32bit boundary */
+               if ((maxsize - currsize) < WORD_SZ)
+                       break;
+       } while (currsize < maxsize);
+
+       clk_disable_unprepare(rng->clk);
+
+       return currsize;
+}
+
+static int msm_rng_init(struct hwrng *hwrng)
+{
+       return msm_rng_enable(hwrng, 1);
+}
+
+static void msm_rng_cleanup(struct hwrng *hwrng)
+{
+       msm_rng_enable(hwrng, 0);
+}
+
+static int msm_rng_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct msm_rng *rng;
+       int ret;
+
+       rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
+       if (!rng)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, rng);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rng->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(rng->base))
+               return PTR_ERR(rng->base);
+
+       rng->clk = devm_clk_get(&pdev->dev, "core");
+       if (IS_ERR(rng->clk))
+               return PTR_ERR(rng->clk);
+
+       rng->hwrng.name = KBUILD_MODNAME,
+       rng->hwrng.init = msm_rng_init,
+       rng->hwrng.cleanup = msm_rng_cleanup,
+       rng->hwrng.read = msm_rng_read,
+
+       ret = hwrng_register(&rng->hwrng);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register hwrng\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int msm_rng_remove(struct platform_device *pdev)
+{
+       struct msm_rng *rng = platform_get_drvdata(pdev);
+
+       hwrng_unregister(&rng->hwrng);
+       return 0;
+}
+
+static const struct of_device_id msm_rng_of_match[] = {
+       { .compatible = "qcom,prng", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, msm_rng_of_match);
+
+static struct platform_driver msm_rng_driver = {
+       .probe = msm_rng_probe,
+       .remove = msm_rng_remove,
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(msm_rng_of_match),
+       }
+};
+module_platform_driver(msm_rng_driver);
+
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_AUTHOR("The Linux Foundation");
+MODULE_DESCRIPTION("Qualcomm MSM random number generator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c
new file mode 100644 (file)
index 0000000..c853e9e
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Juha Yrjola <juha.yrjola@solidboot.com>
+ *
+ * Copyright (C) 2013 Pali Rohár <pali.rohar@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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/random.h>
+#include <linux/hw_random.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#define RNG_RESET                      0x01
+#define RNG_GEN_PRNG_HW_INIT           0x02
+#define RNG_GEN_HW                     0x08
+
+/* param1: ptr, param2: count, param3: flag */
+static u32 (*omap3_rom_rng_call)(u32, u32, u32);
+
+static struct timer_list idle_timer;
+static int rng_idle;
+static struct clk *rng_clk;
+
+static void omap3_rom_rng_idle(unsigned long data)
+{
+       int r;
+
+       r = omap3_rom_rng_call(0, 0, RNG_RESET);
+       if (r != 0) {
+               pr_err("reset failed: %d\n", r);
+               return;
+       }
+       clk_disable_unprepare(rng_clk);
+       rng_idle = 1;
+}
+
+static int omap3_rom_rng_get_random(void *buf, unsigned int count)
+{
+       u32 r;
+       u32 ptr;
+
+       del_timer_sync(&idle_timer);
+       if (rng_idle) {
+               clk_prepare_enable(rng_clk);
+               r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT);
+               if (r != 0) {
+                       clk_disable_unprepare(rng_clk);
+                       pr_err("HW init failed: %d\n", r);
+                       return -EIO;
+               }
+               rng_idle = 0;
+       }
+
+       ptr = virt_to_phys(buf);
+       r = omap3_rom_rng_call(ptr, count, RNG_GEN_HW);
+       mod_timer(&idle_timer, jiffies + msecs_to_jiffies(500));
+       if (r != 0)
+               return -EINVAL;
+       return 0;
+}
+
+static int omap3_rom_rng_data_present(struct hwrng *rng, int wait)
+{
+       return 1;
+}
+
+static int omap3_rom_rng_data_read(struct hwrng *rng, u32 *data)
+{
+       int r;
+
+       r = omap3_rom_rng_get_random(data, 4);
+       if (r < 0)
+               return r;
+       return 4;
+}
+
+static struct hwrng omap3_rom_rng_ops = {
+       .name           = "omap3-rom",
+       .data_present   = omap3_rom_rng_data_present,
+       .data_read      = omap3_rom_rng_data_read,
+};
+
+static int omap3_rom_rng_probe(struct platform_device *pdev)
+{
+       pr_info("initializing\n");
+
+       omap3_rom_rng_call = pdev->dev.platform_data;
+       if (!omap3_rom_rng_call) {
+               pr_err("omap3_rom_rng_call is NULL\n");
+               return -EINVAL;
+       }
+
+       setup_timer(&idle_timer, omap3_rom_rng_idle, 0);
+       rng_clk = clk_get(&pdev->dev, "ick");
+       if (IS_ERR(rng_clk)) {
+               pr_err("unable to get RNG clock\n");
+               return PTR_ERR(rng_clk);
+       }
+
+       /* Leave the RNG in reset state. */
+       clk_prepare_enable(rng_clk);
+       omap3_rom_rng_idle(0);
+
+       return hwrng_register(&omap3_rom_rng_ops);
+}
+
+static int omap3_rom_rng_remove(struct platform_device *pdev)
+{
+       hwrng_unregister(&omap3_rom_rng_ops);
+       clk_disable_unprepare(rng_clk);
+       clk_put(rng_clk);
+       return 0;
+}
+
+static struct platform_driver omap3_rom_rng_driver = {
+       .driver = {
+               .name           = "omap3-rom-rng",
+               .owner          = THIS_MODULE,
+       },
+       .probe          = omap3_rom_rng_probe,
+       .remove         = omap3_rom_rng_remove,
+};
+
+module_platform_driver(omap3_rom_rng_driver);
+
+MODULE_ALIAS("platform:omap3-rom-rng");
+MODULE_AUTHOR("Juha Yrjola");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_LICENSE("GPL");
index b761459a3436c25d31321f5bb46037f7faaf9292..ab7ffdec0ec3545a7ec6f940b32cff396d153183 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/hw_random.h>
 #include <asm/vio.h>
 
-#define MODULE_NAME "pseries-rng"
 
 static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
 {
@@ -55,7 +54,7 @@ static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
 };
 
 static struct hwrng pseries_rng = {
-       .name           = MODULE_NAME,
+       .name           = KBUILD_MODNAME,
        .data_read      = pseries_rng_data_read,
 };
 
@@ -78,7 +77,7 @@ static struct vio_device_id pseries_rng_driver_ids[] = {
 MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids);
 
 static struct vio_driver pseries_rng_driver = {
-       .name = MODULE_NAME,
+       .name = KBUILD_MODNAME,
        .probe = pseries_rng_probe,
        .remove = pseries_rng_remove,
        .get_desired_dma = pseries_rng_get_desired_dma,
index d2120ba8f3f962444dee50ba69fbd57ec16a716a..73ce739f8e196c5ce154c6c186035d28124f4b1d 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 e737772ad69a8103312e596cd0e24ce00d759e22..de5a6dcfb3e242ec4aa5b6742c73c7700e32d551 100644 (file)
@@ -221,7 +221,7 @@ static void __exit mod_exit(void)
 module_init(mod_init);
 module_exit(mod_exit);
 
-static struct x86_cpu_id via_rng_cpu_id[] = {
+static struct x86_cpu_id __maybe_unused via_rng_cpu_id[] = {
        X86_FEATURE_MATCH(X86_FEATURE_XSTORE),
        {}
 };
index ef46a9cfd832a5ce48a3f720c62845a2171b9839..c12398d1517c5cb35711b9c1770d6a4b1eb2ca51 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 4fe5609eeb72beb69e31ed1e7832585eafe0c591..429b75bb60e810946db46b189c4f7f2e173a204f 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];
@@ -916,6 +1035,17 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
        for (i = 0; i < r->poolinfo->poolwords; i += 16)
                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
@@ -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,
        },
        {
@@ -1443,6 +1599,13 @@ struct ctl_table random_table[] = {
                .extra1         = &min_write_thresh,
                .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,
index 94c0c74434eac641a3ddfcd18324913ecedeaafa..1a65838888cdbb37ec2551fd132c7c91cc66c511 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 eb41ff97d0ad13f577ff942364ffe8314a5a2999..b80a4000daeee7b72f7f945637544312473edeb6 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 e3c974a6c522028b96c2df72ab1eb053945b1f43..6ae41d3376302adee51c8be4599e4891bffedf16 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 a7bfc176ed4316bc8319220f1b73848bc9d74f06..f32847872193ad7acdf67bef8921c9f48d474854 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 99d6820c611db2e0f0f344c9cf701cfc02b0505d..c9a528d25d22001141b5809fa255fbc3d1d42154 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 84ddc557b8f8689cd1af8e04ed825e42283fd442..59f7cb28260b4f4c23af010f7615befdd6ed7d23 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 b8735de8ce956a3d19fd3ac5b9a5df77c57d3a22..fefd2aa5c81e4aaa6f9069eb26000f9152acface 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 5bb8e2ddd3b3b9b3a51557a605e8675a8f5c3e64..a0d6ceb5d00551b2c10f4341d1b5b5bd2d812e0b 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 56b07c35a13e173bf1cc5d10321d30d5a11f259e..2783a42aa73295406bee2ff845f5ae08a2d9c738 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 2168d15bc728e22c99a61976de5050c26dc33cbe..8e562dc656016cd9c4dbeaaa3dac0a96a02d0f26 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 5796d0157ce0c3bbd82c662bf61f8035e9d9daf8..1b74459c072399109d1730a6d60471e64ffcc48b 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 94c280d36e8b3bfaea00ee36c4fe3215818b3c86..c8ff4df81779f3b66a3e62abab055b4e404b81e7 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 b79cf3e1b793dca8f652067718d4ece3c0b3335d..feea87cc6b8fb60a5972be32fee027697006ddf2 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 0e1d89b4321b7de9b4bcb49e96f1363c9498e44c..d9e3f671c2ea634012982c2228493cba8eb71903 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 c73fc2b74de2a1dd0665bde5693b1fce03f6d6c5..18c5b9b16645dfa49a218d4b12253240f97310b7 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 6897ad85b0467a8200c3a529e5dee14344c2afbf..d369349eeaab2df6fe768469470ad6de39583889 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 218460fcd2e4f9294d24b84680f173f838c11332..25a70d06c5bf243efe85ff3ed2dfbd2a7cc590df 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 0806c31e57645a287490c4d851d2760cd9484ca0..e6be63561fa699a28053c7528056dcd08ffb2285 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 7b6dc06b1bd4958bea2be16b6a13e5c209953346..f3c22874da753c07dd5f12d58f4b85a6438a2de3 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 f2c75065ce198694428d631baef388ad8e9aa6c6..dfd1643b0b2ff86f229b8e8726fee437c3e1e618 100644 (file)
@@ -157,4 +157,3 @@ err_moutcore:
        pr_debug("%s: failed initialization\n", __func__);
        return -EINVAL;
 }
-EXPORT_SYMBOL(exynos4210_cpufreq_init);
index 8683304ce62cc4dba0c947e2be6049e1b94ffcaf..efad5e657f6f95d9729b33469b2a3f018eaa798c 100644 (file)
@@ -211,4 +211,3 @@ err_moutcore:
        pr_debug("%s: failed initialization\n", __func__);
        return -EINVAL;
 }
-EXPORT_SYMBOL(exynos4x12_cpufreq_init);
index 9fae466d7746a5fed781edb17f4838b6931ed8f0..8feda86fe42c5b5b86c5ac53b02c4d47216fd101 100644 (file)
@@ -236,4 +236,3 @@ err_moutcore:
        pr_err("%s: failed initialization\n", __func__);
        return -EINVAL;
 }
-EXPORT_SYMBOL(exynos5250_cpufreq_init);
index be6d14307aa81a7fa3e3626953b5a43b514933e2..a0acd0bfba40a361f3ea9eaafc42db928968dea7 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 f42df7ec03c53a00838839861567de2e0d8060fa..b7309c37033d57b64d37ca3b93354f0b2848102d 100644 (file)
@@ -142,10 +142,8 @@ static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
 
        mutex_lock(&tegra_cpu_lock);
 
-       if (is_suspended) {
-               ret = -EBUSY;
+       if (is_suspended)
                goto out;
-       }
 
        freq = freq_table[index].frequency;
 
index ca89f6b84b068c1ecc770eededee50bf8c1d55aa..e7555ff4cafdb4b0444a09f2de4b2b153f9e78e9 100644 (file)
@@ -4,16 +4,29 @@ config CRYPTO_DEV_FSL_CAAM
        help
          Enables the driver module for Freescale's Cryptographic Accelerator
          and Assurance Module (CAAM), also known as the SEC version 4 (SEC4).
-         This module adds a job ring operation interface, and configures h/w
+         This module creates job ring devices, and configures h/w
          to operate as a DPAA component automatically, depending
          on h/w feature availability.
 
          To compile this driver as a module, choose M here: the module
          will be called caam.
 
+config CRYPTO_DEV_FSL_CAAM_JR
+       tristate "Freescale CAAM Job Ring driver backend"
+       depends on CRYPTO_DEV_FSL_CAAM
+       default y
+       help
+         Enables the driver module for Job Rings which are part of
+         Freescale's Cryptographic Accelerator
+         and Assurance Module (CAAM). This module adds a job ring operation
+         interface.
+
+         To compile this driver as a module, choose M here: the module
+         will be called caam_jr.
+
 config CRYPTO_DEV_FSL_CAAM_RINGSIZE
        int "Job Ring size"
-       depends on CRYPTO_DEV_FSL_CAAM
+       depends on CRYPTO_DEV_FSL_CAAM_JR
        range 2 9
        default "9"
        help
@@ -31,7 +44,7 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE
 
 config CRYPTO_DEV_FSL_CAAM_INTC
        bool "Job Ring interrupt coalescing"
-       depends on CRYPTO_DEV_FSL_CAAM
+       depends on CRYPTO_DEV_FSL_CAAM_JR
        default n
        help
          Enable the Job Ring's interrupt coalescing feature.
@@ -62,7 +75,7 @@ config CRYPTO_DEV_FSL_CAAM_INTC_TIME_THLD
 
 config CRYPTO_DEV_FSL_CAAM_CRYPTO_API
        tristate "Register algorithm implementations with the Crypto API"
-       depends on CRYPTO_DEV_FSL_CAAM
+       depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
        default y
        select CRYPTO_ALGAPI
        select CRYPTO_AUTHENC
@@ -76,7 +89,7 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API
 
 config CRYPTO_DEV_FSL_CAAM_AHASH_API
        tristate "Register hash algorithm implementations with Crypto API"
-       depends on CRYPTO_DEV_FSL_CAAM
+       depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
        default y
        select CRYPTO_HASH
        help
@@ -88,7 +101,7 @@ config CRYPTO_DEV_FSL_CAAM_AHASH_API
 
 config CRYPTO_DEV_FSL_CAAM_RNG_API
        tristate "Register caam device for hwrng API"
-       depends on CRYPTO_DEV_FSL_CAAM
+       depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
        default y
        select CRYPTO_RNG
        select HW_RANDOM
index d56bd0ec65d877ca9f2989ad77173214cc537868..550758a333e7c8e920a422da0cc20fc8807476d1 100644 (file)
@@ -6,8 +6,10 @@ ifeq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_DEBUG), y)
 endif
 
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
 
-caam-objs := ctrl.o jr.o error.o key_gen.o
+caam-objs := ctrl.o
+caam_jr-objs := jr.o key_gen.o error.o
index 7c63b72ecd750f381fef66e8baccc7955351b1b3..4f44b71b9e24a14a6bc5f99c37e4184970dbb035 100644 (file)
@@ -86,6 +86,7 @@
 #else
 #define debug(format, arg...)
 #endif
+static struct list_head alg_list;
 
 /* Set DK bit in class 1 operation if shared */
 static inline void append_dec_op1(u32 *desc, u32 type)
@@ -2057,7 +2058,6 @@ static struct caam_alg_template driver_algs[] = {
 
 struct caam_crypto_alg {
        struct list_head entry;
-       struct device *ctrldev;
        int class1_alg_type;
        int class2_alg_type;
        int alg_op;
@@ -2070,14 +2070,12 @@ static int caam_cra_init(struct crypto_tfm *tfm)
        struct caam_crypto_alg *caam_alg =
                 container_of(alg, struct caam_crypto_alg, crypto_alg);
        struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct caam_drv_private *priv = dev_get_drvdata(caam_alg->ctrldev);
-       int tgt_jr = atomic_inc_return(&priv->tfm_count);
 
-       /*
-        * distribute tfms across job rings to ensure in-order
-        * crypto request processing per tfm
-        */
-       ctx->jrdev = priv->jrdev[(tgt_jr / 2) % priv->total_jobrs];
+       ctx->jrdev = caam_jr_alloc();
+       if (IS_ERR(ctx->jrdev)) {
+               pr_err("Job Ring Device allocation for transform failed\n");
+               return PTR_ERR(ctx->jrdev);
+       }
 
        /* copy descriptor header template value */
        ctx->class1_alg_type = OP_TYPE_CLASS1_ALG | caam_alg->class1_alg_type;
@@ -2104,44 +2102,26 @@ static void caam_cra_exit(struct crypto_tfm *tfm)
                dma_unmap_single(ctx->jrdev, ctx->sh_desc_givenc_dma,
                                 desc_bytes(ctx->sh_desc_givenc),
                                 DMA_TO_DEVICE);
+
+       caam_jr_free(ctx->jrdev);
 }
 
 static void __exit caam_algapi_exit(void)
 {
 
-       struct device_node *dev_node;
-       struct platform_device *pdev;
-       struct device *ctrldev;
-       struct caam_drv_private *priv;
        struct caam_crypto_alg *t_alg, *n;
 
-       dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-       if (!dev_node) {
-               dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
-               if (!dev_node)
-                       return;
-       }
-
-       pdev = of_find_device_by_node(dev_node);
-       if (!pdev)
-               return;
-
-       ctrldev = &pdev->dev;
-       of_node_put(dev_node);
-       priv = dev_get_drvdata(ctrldev);
-
-       if (!priv->alg_list.next)
+       if (!alg_list.next)
                return;
 
-       list_for_each_entry_safe(t_alg, n, &priv->alg_list, entry) {
+       list_for_each_entry_safe(t_alg, n, &alg_list, entry) {
                crypto_unregister_alg(&t_alg->crypto_alg);
                list_del(&t_alg->entry);
                kfree(t_alg);
        }
 }
 
-static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev,
-                                             struct caam_alg_template
+static struct caam_crypto_alg *caam_alg_alloc(struct caam_alg_template
                                              *template)
 {
        struct caam_crypto_alg *t_alg;
@@ -2149,7 +2129,7 @@ static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev,
 
        t_alg = kzalloc(sizeof(struct caam_crypto_alg), GFP_KERNEL);
        if (!t_alg) {
-               dev_err(ctrldev, "failed to allocate t_alg\n");
+               pr_err("failed to allocate t_alg\n");
                return ERR_PTR(-ENOMEM);
        }
 
@@ -2181,62 +2161,39 @@ static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev,
        t_alg->class1_alg_type = template->class1_alg_type;
        t_alg->class2_alg_type = template->class2_alg_type;
        t_alg->alg_op = template->alg_op;
-       t_alg->ctrldev = ctrldev;
 
        return t_alg;
 }
 
 static int __init caam_algapi_init(void)
 {
-       struct device_node *dev_node;
-       struct platform_device *pdev;
-       struct device *ctrldev;
-       struct caam_drv_private *priv;
        int i = 0, err = 0;
 
-       dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-       if (!dev_node) {
-               dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
-               if (!dev_node)
-                       return -ENODEV;
-       }
-
-       pdev = of_find_device_by_node(dev_node);
-       if (!pdev)
-               return -ENODEV;
-
-       ctrldev = &pdev->dev;
-       priv = dev_get_drvdata(ctrldev);
-       of_node_put(dev_node);
-
-       INIT_LIST_HEAD(&priv->alg_list);
-
-       atomic_set(&priv->tfm_count, -1);
+       INIT_LIST_HEAD(&alg_list);
 
        /* register crypto algorithms the device supports */
        for (i = 0; i < ARRAY_SIZE(driver_algs); i++) {
                /* TODO: check if h/w supports alg */
                struct caam_crypto_alg *t_alg;
 
-               t_alg = caam_alg_alloc(ctrldev, &driver_algs[i]);
+               t_alg = caam_alg_alloc(&driver_algs[i]);
                if (IS_ERR(t_alg)) {
                        err = PTR_ERR(t_alg);
-                       dev_warn(ctrldev, "%s alg allocation failed\n",
-                                driver_algs[i].driver_name);
+                       pr_warn("%s alg allocation failed\n",
+                               driver_algs[i].driver_name);
                        continue;
                }
 
                err = crypto_register_alg(&t_alg->crypto_alg);
                if (err) {
-                       dev_warn(ctrldev, "%s alg registration failed\n",
+                       pr_warn("%s alg registration failed\n",
                                t_alg->crypto_alg.cra_driver_name);
                        kfree(t_alg);
                } else
-                       list_add_tail(&t_alg->entry, &priv->alg_list);
+                       list_add_tail(&t_alg->entry, &alg_list);
        }
-       if (!list_empty(&priv->alg_list))
-               dev_info(ctrldev, "%s algorithms registered in /proc/crypto\n",
-                        (char *)of_get_property(dev_node, "compatible", NULL));
+       if (!list_empty(&alg_list))
+               pr_info("caam algorithms registered in /proc/crypto\n");
 
        return err;
 }
index e732bd962e98cc715db6463c7587dbb2511707e3..0378328f47a775b368b795cd3d7adc9b678fd31b 100644 (file)
@@ -94,6 +94,9 @@
 #define debug(format, arg...)
 #endif
 
+
+static struct list_head hash_list;
+
 /* ahash per-session context */
 struct caam_hash_ctx {
        struct device *jrdev;
@@ -1653,7 +1656,6 @@ static struct caam_hash_template driver_hash[] = {
 
 struct caam_hash_alg {
        struct list_head entry;
-       struct device *ctrldev;
        int alg_type;
        int alg_op;
        struct ahash_alg ahash_alg;
@@ -1670,7 +1672,6 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm)
        struct caam_hash_alg *caam_hash =
                 container_of(alg, struct caam_hash_alg, ahash_alg);
        struct caam_hash_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct caam_drv_private *priv = dev_get_drvdata(caam_hash->ctrldev);
        /* Sizes for MDHA running digests: MD5, SHA1, 224, 256, 384, 512 */
        static const u8 runninglen[] = { HASH_MSG_LEN + MD5_DIGEST_SIZE,
                                         HASH_MSG_LEN + SHA1_DIGEST_SIZE,
@@ -1678,15 +1679,17 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm)
                                         HASH_MSG_LEN + SHA256_DIGEST_SIZE,
                                         HASH_MSG_LEN + 64,
                                         HASH_MSG_LEN + SHA512_DIGEST_SIZE };
-       int tgt_jr = atomic_inc_return(&priv->tfm_count);
        int ret = 0;
 
        /*
-        * distribute tfms across job rings to ensure in-order
+        * Get a Job ring from Job Ring driver to ensure in-order
         * crypto request processing per tfm
         */
-       ctx->jrdev = priv->jrdev[tgt_jr % priv->total_jobrs];
-
+       ctx->jrdev = caam_jr_alloc();
+       if (IS_ERR(ctx->jrdev)) {
+               pr_err("Job Ring Device allocation for transform failed\n");
+               return PTR_ERR(ctx->jrdev);
+       }
        /* copy descriptor header template value */
        ctx->alg_type = OP_TYPE_CLASS2_ALG | caam_hash->alg_type;
        ctx->alg_op = OP_TYPE_CLASS2_ALG | caam_hash->alg_op;
@@ -1729,35 +1732,18 @@ static void caam_hash_cra_exit(struct crypto_tfm *tfm)
            !dma_mapping_error(ctx->jrdev, ctx->sh_desc_finup_dma))
                dma_unmap_single(ctx->jrdev, ctx->sh_desc_finup_dma,
                                 desc_bytes(ctx->sh_desc_finup), DMA_TO_DEVICE);
+
+       caam_jr_free(ctx->jrdev);
 }
 
 static void __exit caam_algapi_hash_exit(void)
 {
-       struct device_node *dev_node;
-       struct platform_device *pdev;
-       struct device *ctrldev;
-       struct caam_drv_private *priv;
        struct caam_hash_alg *t_alg, *n;
 
-       dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-       if (!dev_node) {
-               dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
-               if (!dev_node)
-                       return;
-       }
-
-       pdev = of_find_device_by_node(dev_node);
-       if (!pdev)
+       if (!hash_list.next)
                return;
 
-       ctrldev = &pdev->dev;
-       of_node_put(dev_node);
-       priv = dev_get_drvdata(ctrldev);
-
-       if (!priv->hash_list.next)
-               return;
-
-       list_for_each_entry_safe(t_alg, n, &priv->hash_list, entry) {
+       list_for_each_entry_safe(t_alg, n, &hash_list, entry) {
                crypto_unregister_ahash(&t_alg->ahash_alg);
                list_del(&t_alg->entry);
                kfree(t_alg);
@@ -1765,7 +1751,7 @@ static void __exit caam_algapi_hash_exit(void)
 }
 
 static struct caam_hash_alg *
-caam_hash_alloc(struct device *ctrldev, struct caam_hash_template *template,
+caam_hash_alloc(struct caam_hash_template *template,
                bool keyed)
 {
        struct caam_hash_alg *t_alg;
@@ -1774,7 +1760,7 @@ caam_hash_alloc(struct device *ctrldev, struct caam_hash_template *template,
 
        t_alg = kzalloc(sizeof(struct caam_hash_alg), GFP_KERNEL);
        if (!t_alg) {
-               dev_err(ctrldev, "failed to allocate t_alg\n");
+               pr_err("failed to allocate t_alg\n");
                return ERR_PTR(-ENOMEM);
        }
 
@@ -1805,37 +1791,15 @@ caam_hash_alloc(struct device *ctrldev, struct caam_hash_template *template,
 
        t_alg->alg_type = template->alg_type;
        t_alg->alg_op = template->alg_op;
-       t_alg->ctrldev = ctrldev;
 
        return t_alg;
 }
 
 static int __init caam_algapi_hash_init(void)
 {
-       struct device_node *dev_node;
-       struct platform_device *pdev;
-       struct device *ctrldev;
-       struct caam_drv_private *priv;
        int i = 0, err = 0;
 
-       dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-       if (!dev_node) {
-               dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
-               if (!dev_node)
-                       return -ENODEV;
-       }
-
-       pdev = of_find_device_by_node(dev_node);
-       if (!pdev)
-               return -ENODEV;
-
-       ctrldev = &pdev->dev;
-       priv = dev_get_drvdata(ctrldev);
-       of_node_put(dev_node);
-
-       INIT_LIST_HEAD(&priv->hash_list);
-
-       atomic_set(&priv->tfm_count, -1);
+       INIT_LIST_HEAD(&hash_list);
 
        /* register crypto algorithms the device supports */
        for (i = 0; i < ARRAY_SIZE(driver_hash); i++) {
@@ -1843,38 +1807,38 @@ static int __init caam_algapi_hash_init(void)
                struct caam_hash_alg *t_alg;
 
                /* register hmac version */
-               t_alg = caam_hash_alloc(ctrldev, &driver_hash[i], true);
+               t_alg = caam_hash_alloc(&driver_hash[i], true);
                if (IS_ERR(t_alg)) {
                        err = PTR_ERR(t_alg);
-                       dev_warn(ctrldev, "%s alg allocation failed\n",
-                                driver_hash[i].driver_name);
+                       pr_warn("%s alg allocation failed\n",
+                               driver_hash[i].driver_name);
                        continue;
                }
 
                err = crypto_register_ahash(&t_alg->ahash_alg);
                if (err) {
-                       dev_warn(ctrldev, "%s alg registration failed\n",
+                       pr_warn("%s alg registration failed\n",
                                t_alg->ahash_alg.halg.base.cra_driver_name);
                        kfree(t_alg);
                } else
-                       list_add_tail(&t_alg->entry, &priv->hash_list);
+                       list_add_tail(&t_alg->entry, &hash_list);
 
                /* register unkeyed version */
-               t_alg = caam_hash_alloc(ctrldev, &driver_hash[i], false);
+               t_alg = caam_hash_alloc(&driver_hash[i], false);
                if (IS_ERR(t_alg)) {
                        err = PTR_ERR(t_alg);
-                       dev_warn(ctrldev, "%s alg allocation failed\n",
-                                driver_hash[i].driver_name);
+                       pr_warn("%s alg allocation failed\n",
+                               driver_hash[i].driver_name);
                        continue;
                }
 
                err = crypto_register_ahash(&t_alg->ahash_alg);
                if (err) {
-                       dev_warn(ctrldev, "%s alg registration failed\n",
+                       pr_warn("%s alg registration failed\n",
                                t_alg->ahash_alg.halg.base.cra_driver_name);
                        kfree(t_alg);
                } else
-                       list_add_tail(&t_alg->entry, &priv->hash_list);
+                       list_add_tail(&t_alg->entry, &hash_list);
        }
 
        return err;
index d1939a9539c06a4204a26b65d3d743d46c2346d3..28486b19fc36b3e837d774ace08a3aa041b8d1e7 100644 (file)
@@ -273,34 +273,23 @@ static struct hwrng caam_rng = {
 
 static void __exit caam_rng_exit(void)
 {
+       caam_jr_free(rng_ctx.jrdev);
        hwrng_unregister(&caam_rng);
 }
 
 static int __init caam_rng_init(void)
 {
-       struct device_node *dev_node;
-       struct platform_device *pdev;
-       struct device *ctrldev;
-       struct caam_drv_private *priv;
-
-       dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-       if (!dev_node) {
-               dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
-               if (!dev_node)
-                       return -ENODEV;
-       }
-
-       pdev = of_find_device_by_node(dev_node);
-       if (!pdev)
-               return -ENODEV;
+       struct device *dev;
 
-       ctrldev = &pdev->dev;
-       priv = dev_get_drvdata(ctrldev);
-       of_node_put(dev_node);
+       dev = caam_jr_alloc();
+       if (IS_ERR(dev)) {
+               pr_err("Job Ring Device allocation for transform failed\n");
+               return PTR_ERR(dev);
+       }
 
-       caam_init_rng(&rng_ctx, priv->jrdev[0]);
+       caam_init_rng(&rng_ctx, dev);
 
-       dev_info(priv->jrdev[0], "registering rng-caam\n");
+       dev_info(dev, "registering rng-caam\n");
        return hwrng_register(&caam_rng);
 }
 
index bc6d820812b6a73e83c6596caa48ae7d475c315a..63fb1af2c43187fe398663869b5877fa16bf8834 100644 (file)
 #include "error.h"
 #include "ctrl.h"
 
-static int caam_remove(struct platform_device *pdev)
-{
-       struct device *ctrldev;
-       struct caam_drv_private *ctrlpriv;
-       struct caam_drv_private_jr *jrpriv;
-       struct caam_full __iomem *topregs;
-       int ring, ret = 0;
-
-       ctrldev = &pdev->dev;
-       ctrlpriv = dev_get_drvdata(ctrldev);
-       topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
-
-       /* shut down JobRs */
-       for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) {
-               ret |= caam_jr_shutdown(ctrlpriv->jrdev[ring]);
-               jrpriv = dev_get_drvdata(ctrlpriv->jrdev[ring]);
-               irq_dispose_mapping(jrpriv->irq);
-       }
-
-       /* Shut down debug views */
-#ifdef CONFIG_DEBUG_FS
-       debugfs_remove_recursive(ctrlpriv->dfs_root);
-#endif
-
-       /* Unmap controller region */
-       iounmap(&topregs->ctrl);
-
-       kfree(ctrlpriv->jrdev);
-       kfree(ctrlpriv);
-
-       return ret;
-}
-
 /*
  * Descriptor to instantiate RNG State Handle 0 in normal mode and
  * load the JDKEK, TDKEK and TDSK registers
  */
-static void build_instantiation_desc(u32 *desc)
+static void build_instantiation_desc(u32 *desc, int handle, int do_sk)
 {
-       u32 *jump_cmd;
+       u32 *jump_cmd, op_flags;
 
        init_job_desc(desc, 0);
 
+       op_flags = OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+                       (handle << OP_ALG_AAI_SHIFT) | OP_ALG_AS_INIT;
+
        /* INIT RNG in non-test mode */
-       append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
-                        OP_ALG_AS_INIT);
+       append_operation(desc, op_flags);
+
+       if (!handle && do_sk) {
+               /*
+                * For SH0, Secure Keys must be generated as well
+                */
+
+               /* wait for done */
+               jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1);
+               set_jump_tgt_here(desc, jump_cmd);
+
+               /*
+                * load 1 to clear written reg:
+                * resets the done interrrupt and returns the RNG to idle.
+                */
+               append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW);
+
+               /* Initialize State Handle  */
+               append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+                                OP_ALG_AAI_RNG4_SK);
+       }
 
-       /* wait for done */
-       jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1);
-       set_jump_tgt_here(desc, jump_cmd);
+       append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TYPE_HALT);
+}
 
-       /*
-        * load 1 to clear written reg:
-        * resets the done interrupt and returns the RNG to idle.
-        */
-       append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW);
+/* Descriptor for deinstantiation of State Handle 0 of the RNG block. */
+static void build_deinstantiation_desc(u32 *desc, int handle)
+{
+       init_job_desc(desc, 0);
 
-       /* generate secure keys (non-test) */
+       /* Uninstantiate State Handle 0 */
        append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
-                        OP_ALG_RNG4_SK);
+                        (handle << OP_ALG_AAI_SHIFT) | OP_ALG_AS_INITFINAL);
+
+       append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TYPE_HALT);
 }
 
-static int instantiate_rng(struct device *ctrldev)
+/*
+ * run_descriptor_deco0 - runs a descriptor on DECO0, under direct control of
+ *                       the software (no JR/QI used).
+ * @ctrldev - pointer to device
+ * @status - descriptor status, after being run
+ *
+ * Return: - 0 if no error occurred
+ *        - -ENODEV if the DECO couldn't be acquired
+ *        - -EAGAIN if an error occurred while executing the descriptor
+ */
+static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc,
+                                       u32 *status)
 {
        struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
        struct caam_full __iomem *topregs;
        unsigned int timeout = 100000;
-       u32 *desc;
-       int i, ret = 0;
-
-       desc = kmalloc(CAAM_CMD_SZ * 6, GFP_KERNEL | GFP_DMA);
-       if (!desc) {
-               dev_err(ctrldev, "can't allocate RNG init descriptor memory\n");
-               return -ENOMEM;
-       }
-       build_instantiation_desc(desc);
+       u32 deco_dbg_reg, flags;
+       int i;
 
        /* Set the bit to request direct access to DECO0 */
        topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
@@ -103,36 +96,219 @@ static int instantiate_rng(struct device *ctrldev)
 
        if (!timeout) {
                dev_err(ctrldev, "failed to acquire DECO 0\n");
-               ret = -EIO;
-               goto out;
+               clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE);
+               return -ENODEV;
        }
 
        for (i = 0; i < desc_len(desc); i++)
-               topregs->deco.descbuf[i] = *(desc + i);
+               wr_reg32(&topregs->deco.descbuf[i], *(desc + i));
+
+       flags = DECO_JQCR_WHL;
+       /*
+        * If the descriptor length is longer than 4 words, then the
+        * FOUR bit in JRCTRL register must be set.
+        */
+       if (desc_len(desc) >= 4)
+               flags |= DECO_JQCR_FOUR;
 
-       wr_reg32(&topregs->deco.jr_ctl_hi, DECO_JQCR_WHL | DECO_JQCR_FOUR);
+       /* Instruct the DECO to execute it */
+       wr_reg32(&topregs->deco.jr_ctl_hi, flags);
 
        timeout = 10000000;
-       while ((rd_reg32(&topregs->deco.desc_dbg) & DECO_DBG_VALID) &&
-                                                                --timeout)
+       do {
+               deco_dbg_reg = rd_reg32(&topregs->deco.desc_dbg);
+               /*
+                * If an error occured in the descriptor, then
+                * the DECO status field will be set to 0x0D
+                */
+               if ((deco_dbg_reg & DESC_DBG_DECO_STAT_MASK) ==
+                   DESC_DBG_DECO_STAT_HOST_ERR)
+                       break;
                cpu_relax();
+       } while ((deco_dbg_reg & DESC_DBG_DECO_STAT_VALID) && --timeout);
 
-       if (!timeout) {
-               dev_err(ctrldev, "failed to instantiate RNG\n");
-               ret = -EIO;
-       }
+       *status = rd_reg32(&topregs->deco.op_status_hi) &
+                 DECO_OP_STATUS_HI_ERR_MASK;
 
+       /* Mark the DECO as free */
        clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE);
-out:
+
+       if (!timeout)
+               return -EAGAIN;
+
+       return 0;
+}
+
+/*
+ * instantiate_rng - builds and executes a descriptor on DECO0,
+ *                  which initializes the RNG block.
+ * @ctrldev - pointer to device
+ * @state_handle_mask - bitmask containing the instantiation status
+ *                     for the RNG4 state handles which exist in
+ *                     the RNG4 block: 1 if it's been instantiated
+ *                     by an external entry, 0 otherwise.
+ * @gen_sk  - generate data to be loaded into the JDKEK, TDKEK and TDSK;
+ *           Caution: this can be done only once; if the keys need to be
+ *           regenerated, a POR is required
+ *
+ * Return: - 0 if no error occurred
+ *        - -ENOMEM if there isn't enough memory to allocate the descriptor
+ *        - -ENODEV if DECO0 couldn't be acquired
+ *        - -EAGAIN if an error occurred when executing the descriptor
+ *           f.i. there was a RNG hardware error due to not "good enough"
+ *           entropy being aquired.
+ */
+static int instantiate_rng(struct device *ctrldev, int state_handle_mask,
+                          int gen_sk)
+{
+       struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
+       struct caam_full __iomem *topregs;
+       struct rng4tst __iomem *r4tst;
+       u32 *desc, status, rdsta_val;
+       int ret = 0, sh_idx;
+
+       topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
+       r4tst = &topregs->ctrl.r4tst[0];
+
+       desc = kmalloc(CAAM_CMD_SZ * 7, GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
+
+       for (sh_idx = 0; sh_idx < RNG4_MAX_HANDLES; sh_idx++) {
+               /*
+                * If the corresponding bit is set, this state handle
+                * was initialized by somebody else, so it's left alone.
+                */
+               if ((1 << sh_idx) & state_handle_mask)
+                       continue;
+
+               /* Create the descriptor for instantiating RNG State Handle */
+               build_instantiation_desc(desc, sh_idx, gen_sk);
+
+               /* Try to run it through DECO0 */
+               ret = run_descriptor_deco0(ctrldev, desc, &status);
+
+               /*
+                * If ret is not 0, or descriptor status is not 0, then
+                * something went wrong. No need to try the next state
+                * handle (if available), bail out here.
+                * Also, if for some reason, the State Handle didn't get
+                * instantiated although the descriptor has finished
+                * without any error (HW optimizations for later
+                * CAAM eras), then try again.
+                */
+               rdsta_val =
+                       rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IFMASK;
+               if (status || !(rdsta_val & (1 << sh_idx)))
+                       ret = -EAGAIN;
+               if (ret)
+                       break;
+
+               dev_info(ctrldev, "Instantiated RNG4 SH%d\n", sh_idx);
+               /* Clear the contents before recreating the descriptor */
+               memset(desc, 0x00, CAAM_CMD_SZ * 7);
+       }
+
        kfree(desc);
+
        return ret;
 }
 
 /*
- * By default, the TRNG runs for 200 clocks per sample;
- * 1600 clocks per sample generates better entropy.
+ * deinstantiate_rng - builds and executes a descriptor on DECO0,
+ *                    which deinitializes the RNG block.
+ * @ctrldev - pointer to device
+ * @state_handle_mask - bitmask containing the instantiation status
+ *                     for the RNG4 state handles which exist in
+ *                     the RNG4 block: 1 if it's been instantiated
+ *
+ * Return: - 0 if no error occurred
+ *        - -ENOMEM if there isn't enough memory to allocate the descriptor
+ *        - -ENODEV if DECO0 couldn't be acquired
+ *        - -EAGAIN if an error occurred when executing the descriptor
  */
-static void kick_trng(struct platform_device *pdev)
+static int deinstantiate_rng(struct device *ctrldev, int state_handle_mask)
+{
+       u32 *desc, status;
+       int sh_idx, ret = 0;
+
+       desc = kmalloc(CAAM_CMD_SZ * 3, GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
+
+       for (sh_idx = 0; sh_idx < RNG4_MAX_HANDLES; sh_idx++) {
+               /*
+                * If the corresponding bit is set, then it means the state
+                * handle was initialized by us, and thus it needs to be
+                * deintialized as well
+                */
+               if ((1 << sh_idx) & state_handle_mask) {
+                       /*
+                        * Create the descriptor for deinstantating this state
+                        * handle
+                        */
+                       build_deinstantiation_desc(desc, sh_idx);
+
+                       /* Try to run it through DECO0 */
+                       ret = run_descriptor_deco0(ctrldev, desc, &status);
+
+                       if (ret || status) {
+                               dev_err(ctrldev,
+                                       "Failed to deinstantiate RNG4 SH%d\n",
+                                       sh_idx);
+                               break;
+                       }
+                       dev_info(ctrldev, "Deinstantiated RNG4 SH%d\n", sh_idx);
+               }
+       }
+
+       kfree(desc);
+
+       return ret;
+}
+
+static int caam_remove(struct platform_device *pdev)
+{
+       struct device *ctrldev;
+       struct caam_drv_private *ctrlpriv;
+       struct caam_full __iomem *topregs;
+       int ring, ret = 0;
+
+       ctrldev = &pdev->dev;
+       ctrlpriv = dev_get_drvdata(ctrldev);
+       topregs = (struct caam_full __iomem *)ctrlpriv->ctrl;
+
+       /* Remove platform devices for JobRs */
+       for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) {
+               if (ctrlpriv->jrpdev[ring])
+                       of_device_unregister(ctrlpriv->jrpdev[ring]);
+       }
+
+       /* De-initialize RNG state handles initialized by this driver. */
+       if (ctrlpriv->rng4_sh_init)
+               deinstantiate_rng(ctrldev, ctrlpriv->rng4_sh_init);
+
+       /* Shut down debug views */
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(ctrlpriv->dfs_root);
+#endif
+
+       /* Unmap controller region */
+       iounmap(&topregs->ctrl);
+
+       kfree(ctrlpriv->jrpdev);
+       kfree(ctrlpriv);
+
+       return ret;
+}
+
+/*
+ * kick_trng - sets the various parameters for enabling the initialization
+ *            of the RNG4 block in CAAM
+ * @pdev - pointer to the platform device
+ * @ent_delay - Defines the length (in system clocks) of each entropy sample.
+ */
+static void kick_trng(struct platform_device *pdev, int ent_delay)
 {
        struct device *ctrldev = &pdev->dev;
        struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
@@ -145,14 +321,31 @@ static void kick_trng(struct platform_device *pdev)
 
        /* put RNG4 into program mode */
        setbits32(&r4tst->rtmctl, RTMCTL_PRGM);
-       /* 1600 clocks per sample */
+
+       /*
+        * Performance-wise, it does not make sense to
+        * set the delay to a value that is lower
+        * than the last one that worked (i.e. the state handles
+        * were instantiated properly. Thus, instead of wasting
+        * time trying to set the values controlling the sample
+        * frequency, the function simply returns.
+        */
+       val = (rd_reg32(&r4tst->rtsdctl) & RTSDCTL_ENT_DLY_MASK)
+             >> RTSDCTL_ENT_DLY_SHIFT;
+       if (ent_delay <= val) {
+               /* put RNG4 into run mode */
+               clrbits32(&r4tst->rtmctl, RTMCTL_PRGM);
+               return;
+       }
+
        val = rd_reg32(&r4tst->rtsdctl);
-       val = (val & ~RTSDCTL_ENT_DLY_MASK) | (1600 << RTSDCTL_ENT_DLY_SHIFT);
+       val = (val & ~RTSDCTL_ENT_DLY_MASK) |
+             (ent_delay << RTSDCTL_ENT_DLY_SHIFT);
        wr_reg32(&r4tst->rtsdctl, val);
-       /* min. freq. count */
-       wr_reg32(&r4tst->rtfrqmin, 400);
-       /* max. freq. count */
-       wr_reg32(&r4tst->rtfrqmax, 6400);
+       /* min. freq. count, equal to 1/4 of the entropy sample length */
+       wr_reg32(&r4tst->rtfrqmin, ent_delay >> 2);
+       /* max. freq. count, equal to 8 times the entropy sample length */
+       wr_reg32(&r4tst->rtfrqmax, ent_delay << 3);
        /* put RNG4 into run mode */
        clrbits32(&r4tst->rtmctl, RTMCTL_PRGM);
 }
@@ -193,7 +386,7 @@ EXPORT_SYMBOL(caam_get_era);
 /* Probe routine for CAAM top (controller) level */
 static int caam_probe(struct platform_device *pdev)
 {
-       int ret, ring, rspec;
+       int ret, ring, rspec, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN;
        u64 caam_id;
        struct device *dev;
        struct device_node *nprop, *np;
@@ -258,8 +451,9 @@ static int caam_probe(struct platform_device *pdev)
                        rspec++;
        }
 
-       ctrlpriv->jrdev = kzalloc(sizeof(struct device *) * rspec, GFP_KERNEL);
-       if (ctrlpriv->jrdev == NULL) {
+       ctrlpriv->jrpdev = kzalloc(sizeof(struct platform_device *) * rspec,
+                                                               GFP_KERNEL);
+       if (ctrlpriv->jrpdev == NULL) {
                iounmap(&topregs->ctrl);
                return -ENOMEM;
        }
@@ -267,13 +461,24 @@ static int caam_probe(struct platform_device *pdev)
        ring = 0;
        ctrlpriv->total_jobrs = 0;
        for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring") {
-               caam_jr_probe(pdev, np, ring);
+               ctrlpriv->jrpdev[ring] =
+                               of_platform_device_create(np, NULL, dev);
+               if (!ctrlpriv->jrpdev[ring]) {
+                       pr_warn("JR%d Platform device creation error\n", ring);
+                       continue;
+               }
                ctrlpriv->total_jobrs++;
                ring++;
        }
        if (!ring) {
                for_each_compatible_node(np, NULL, "fsl,sec4.0-job-ring") {
-                       caam_jr_probe(pdev, np, ring);
+                       ctrlpriv->jrpdev[ring] =
+                               of_platform_device_create(np, NULL, dev);
+                       if (!ctrlpriv->jrpdev[ring]) {
+                               pr_warn("JR%d Platform device creation error\n",
+                                       ring);
+                               continue;
+                       }
                        ctrlpriv->total_jobrs++;
                        ring++;
                }
@@ -299,16 +504,55 @@ static int caam_probe(struct platform_device *pdev)
 
        /*
         * If SEC has RNG version >= 4 and RNG state handle has not been
-        * already instantiated ,do RNG instantiation
+        * already instantiateddo RNG instantiation
         */
-       if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4 &&
-           !(rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IF0)) {
-               kick_trng(pdev);
-               ret = instantiate_rng(dev);
+       if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4) {
+               ctrlpriv->rng4_sh_init =
+                       rd_reg32(&topregs->ctrl.r4tst[0].rdsta);
+               /*
+                * If the secure keys (TDKEK, JDKEK, TDSK), were already
+                * generated, signal this to the function that is instantiating
+                * the state handles. An error would occur if RNG4 attempts
+                * to regenerate these keys before the next POR.
+                */
+               gen_sk = ctrlpriv->rng4_sh_init & RDSTA_SKVN ? 0 : 1;
+               ctrlpriv->rng4_sh_init &= RDSTA_IFMASK;
+               do {
+                       int inst_handles =
+                               rd_reg32(&topregs->ctrl.r4tst[0].rdsta) &
+                                                               RDSTA_IFMASK;
+                       /*
+                        * If either SH were instantiated by somebody else
+                        * (e.g. u-boot) then it is assumed that the entropy
+                        * parameters are properly set and thus the function
+                        * setting these (kick_trng(...)) is skipped.
+                        * Also, if a handle was instantiated, do not change
+                        * the TRNG parameters.
+                        */
+                       if (!(ctrlpriv->rng4_sh_init || inst_handles)) {
+                               kick_trng(pdev, ent_delay);
+                               ent_delay += 400;
+                       }
+                       /*
+                        * if instantiate_rng(...) fails, the loop will rerun
+                        * and the kick_trng(...) function will modfiy the
+                        * upper and lower limits of the entropy sampling
+                        * interval, leading to a sucessful initialization of
+                        * the RNG.
+                        */
+                       ret = instantiate_rng(dev, inst_handles,
+                                             gen_sk);
+               } while ((ret == -EAGAIN) && (ent_delay < RTSDCTL_ENT_DLY_MAX));
                if (ret) {
+                       dev_err(dev, "failed to instantiate RNG");
                        caam_remove(pdev);
                        return ret;
                }
+               /*
+                * Set handles init'ed by this module as the complement of the
+                * already initialized ones
+                */
+               ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_IFMASK;
 
                /* Enable RDB bit so that RNG works faster */
                setbits32(&topregs->ctrl.scfgr, SCFGR_RDBENABLE);
index 53b296f78b0d2a588fe1cc3bb4bde067c05a0eda..7e4500f18df6f06de602a3d44ff6d88c4ca70ec5 100644 (file)
@@ -1155,8 +1155,15 @@ struct sec4_sg_entry {
 
 /* randomizer AAI set */
 #define OP_ALG_AAI_RNG         (0x00 << OP_ALG_AAI_SHIFT)
-#define OP_ALG_AAI_RNG_NOZERO  (0x10 << OP_ALG_AAI_SHIFT)
-#define OP_ALG_AAI_RNG_ODD     (0x20 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG_NZB     (0x10 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG_OBP     (0x20 << OP_ALG_AAI_SHIFT)
+
+/* RNG4 AAI set */
+#define OP_ALG_AAI_RNG4_SH_0   (0x00 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG4_SH_1   (0x01 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG4_PS     (0x40 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG4_AI     (0x80 << OP_ALG_AAI_SHIFT)
+#define OP_ALG_AAI_RNG4_SK     (0x100 << OP_ALG_AAI_SHIFT)
 
 /* hmac/smac AAI set */
 #define OP_ALG_AAI_HASH                (0x00 << OP_ALG_AAI_SHIFT)
@@ -1178,12 +1185,6 @@ struct sec4_sg_entry {
 #define OP_ALG_AAI_GSM         (0x10 << OP_ALG_AAI_SHIFT)
 #define OP_ALG_AAI_EDGE                (0x20 << OP_ALG_AAI_SHIFT)
 
-/* RNG4 set */
-#define OP_ALG_RNG4_SHIFT      4
-#define OP_ALG_RNG4_MASK       (0x1f3 << OP_ALG_RNG4_SHIFT)
-
-#define OP_ALG_RNG4_SK         (0x100 << OP_ALG_RNG4_SHIFT)
-
 #define OP_ALG_AS_SHIFT                2
 #define OP_ALG_AS_MASK         (0x3 << OP_ALG_AS_SHIFT)
 #define OP_ALG_AS_UPDATE       (0 << OP_ALG_AS_SHIFT)
index 34c4b9f7fbfae414a1578e37da245fd9119ac8fd..6d85fcc5bd0a48977467eaa058609ff882fff26e 100644 (file)
@@ -37,13 +37,16 @@ struct caam_jrentry_info {
 
 /* Private sub-storage for a single JobR */
 struct caam_drv_private_jr {
-       struct device *parentdev;       /* points back to controller dev */
-       struct platform_device *jr_pdev;/* points to platform device for JR */
+       struct list_head        list_node;      /* Job Ring device list */
+       struct device           *dev;
        int ridx;
        struct caam_job_ring __iomem *rregs;    /* JobR's register space */
        struct tasklet_struct irqtask;
        int irq;                        /* One per queue */
 
+       /* Number of scatterlist crypt transforms active on the JobR */
+       atomic_t tfm_count ____cacheline_aligned;
+
        /* Job ring info */
        int ringsize;   /* Size of rings (assume input = output) */
        struct caam_jrentry_info *entinfo;      /* Alloc'ed 1 per ring entry */
@@ -63,7 +66,7 @@ struct caam_drv_private_jr {
 struct caam_drv_private {
 
        struct device *dev;
-       struct device **jrdev; /* Alloc'ed array per sub-device */
+       struct platform_device **jrpdev; /* Alloc'ed array per sub-device */
        struct platform_device *pdev;
 
        /* Physical-presence section */
@@ -80,12 +83,11 @@ struct caam_drv_private {
        u8 qi_present;          /* Nonzero if QI present in device */
        int secvio_irq;         /* Security violation interrupt number */
 
-       /* which jr allocated to scatterlist crypto */
-       atomic_t tfm_count ____cacheline_aligned;
-       /* list of registered crypto algorithms (mk generic context handle?) */
-       struct list_head alg_list;
-       /* list of registered hash algorithms (mk generic context handle?) */
-       struct list_head hash_list;
+#define        RNG4_MAX_HANDLES 2
+       /* RNG4 block */
+       u32 rng4_sh_init;       /* This bitmap shows which of the State
+                                  Handles of the RNG4 block are initialized
+                                  by this driver */
 
        /*
         * debugfs entries for developer view into driver/device
index bdb786d5a5e5b0216b968a833592d8aef7c96d8d..d23356d20e1ca430e5993aaf77b88b39b82fe651 100644 (file)
 #include "desc.h"
 #include "intern.h"
 
+struct jr_driver_data {
+       /* List of Physical JobR's with the Driver */
+       struct list_head        jr_list;
+       spinlock_t              jr_alloc_lock;  /* jr_list lock */
+} ____cacheline_aligned;
+
+static struct jr_driver_data driver_data;
+
+static int caam_reset_hw_jr(struct device *dev)
+{
+       struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
+       unsigned int timeout = 100000;
+
+       /*
+        * mask interrupts since we are going to poll
+        * for reset completion status
+        */
+       setbits32(&jrp->rregs->rconfig_lo, JRCFG_IMSK);
+
+       /* initiate flush (required prior to reset) */
+       wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
+       while (((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) ==
+               JRINT_ERR_HALT_INPROGRESS) && --timeout)
+               cpu_relax();
+
+       if ((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) !=
+           JRINT_ERR_HALT_COMPLETE || timeout == 0) {
+               dev_err(dev, "failed to flush job ring %d\n", jrp->ridx);
+               return -EIO;
+       }
+
+       /* initiate reset */
+       timeout = 100000;
+       wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
+       while ((rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET) && --timeout)
+               cpu_relax();
+
+       if (timeout == 0) {
+               dev_err(dev, "failed to reset job ring %d\n", jrp->ridx);
+               return -EIO;
+       }
+
+       /* unmask interrupts */
+       clrbits32(&jrp->rregs->rconfig_lo, JRCFG_IMSK);
+
+       return 0;
+}
+
+/*
+ * Shutdown JobR independent of platform property code
+ */
+int caam_jr_shutdown(struct device *dev)
+{
+       struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
+       dma_addr_t inpbusaddr, outbusaddr;
+       int ret;
+
+       ret = caam_reset_hw_jr(dev);
+
+       tasklet_kill(&jrp->irqtask);
+
+       /* Release interrupt */
+       free_irq(jrp->irq, dev);
+
+       /* Free rings */
+       inpbusaddr = rd_reg64(&jrp->rregs->inpring_base);
+       outbusaddr = rd_reg64(&jrp->rregs->outring_base);
+       dma_free_coherent(dev, sizeof(dma_addr_t) * JOBR_DEPTH,
+                         jrp->inpring, inpbusaddr);
+       dma_free_coherent(dev, sizeof(struct jr_outentry) * JOBR_DEPTH,
+                         jrp->outring, outbusaddr);
+       kfree(jrp->entinfo);
+
+       return ret;
+}
+
+static int caam_jr_remove(struct platform_device *pdev)
+{
+       int ret;
+       struct device *jrdev;
+       struct caam_drv_private_jr *jrpriv;
+
+       jrdev = &pdev->dev;
+       jrpriv = dev_get_drvdata(jrdev);
+
+       /*
+        * Return EBUSY if job ring already allocated.
+        */
+       if (atomic_read(&jrpriv->tfm_count)) {
+               dev_err(jrdev, "Device is busy\n");
+               return -EBUSY;
+       }
+
+       /* Remove the node from Physical JobR list maintained by driver */
+       spin_lock(&driver_data.jr_alloc_lock);
+       list_del(&jrpriv->list_node);
+       spin_unlock(&driver_data.jr_alloc_lock);
+
+       /* Release ring */
+       ret = caam_jr_shutdown(jrdev);
+       if (ret)
+               dev_err(jrdev, "Failed to shut down job ring\n");
+       irq_dispose_mapping(jrpriv->irq);
+
+       return ret;
+}
+
 /* Main per-ring interrupt handler */
 static irqreturn_t caam_jr_interrupt(int irq, void *st_dev)
 {
@@ -127,6 +234,59 @@ static void caam_jr_dequeue(unsigned long devarg)
        clrbits32(&jrp->rregs->rconfig_lo, JRCFG_IMSK);
 }
 
+/**
+ * caam_jr_alloc() - Alloc a job ring for someone to use as needed.
+ *
+ * returns :  pointer to the newly allocated physical
+ *           JobR dev can be written to if successful.
+ **/
+struct device *caam_jr_alloc(void)
+{
+       struct caam_drv_private_jr *jrpriv, *min_jrpriv = NULL;
+       struct device *dev = NULL;
+       int min_tfm_cnt = INT_MAX;
+       int tfm_cnt;
+
+       spin_lock(&driver_data.jr_alloc_lock);
+
+       if (list_empty(&driver_data.jr_list)) {
+               spin_unlock(&driver_data.jr_alloc_lock);
+               return ERR_PTR(-ENODEV);
+       }
+
+       list_for_each_entry(jrpriv, &driver_data.jr_list, list_node) {
+               tfm_cnt = atomic_read(&jrpriv->tfm_count);
+               if (tfm_cnt < min_tfm_cnt) {
+                       min_tfm_cnt = tfm_cnt;
+                       min_jrpriv = jrpriv;
+               }
+               if (!min_tfm_cnt)
+                       break;
+       }
+
+       if (min_jrpriv) {
+               atomic_inc(&min_jrpriv->tfm_count);
+               dev = min_jrpriv->dev;
+       }
+       spin_unlock(&driver_data.jr_alloc_lock);
+
+       return dev;
+}
+EXPORT_SYMBOL(caam_jr_alloc);
+
+/**
+ * caam_jr_free() - Free the Job Ring
+ * @rdev     - points to the dev that identifies the Job ring to
+ *             be released.
+ **/
+void caam_jr_free(struct device *rdev)
+{
+       struct caam_drv_private_jr *jrpriv = dev_get_drvdata(rdev);
+
+       atomic_dec(&jrpriv->tfm_count);
+}
+EXPORT_SYMBOL(caam_jr_free);
+
 /**
  * caam_jr_enqueue() - Enqueue a job descriptor head. Returns 0 if OK,
  * -EBUSY if the queue is full, -EIO if it cannot map the caller's
@@ -207,46 +367,6 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
 }
 EXPORT_SYMBOL(caam_jr_enqueue);
 
-static int caam_reset_hw_jr(struct device *dev)
-{
-       struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
-       unsigned int timeout = 100000;
-
-       /*
-        * mask interrupts since we are going to poll
-        * for reset completion status
-        */
-       setbits32(&jrp->rregs->rconfig_lo, JRCFG_IMSK);
-
-       /* initiate flush (required prior to reset) */
-       wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
-       while (((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) ==
-               JRINT_ERR_HALT_INPROGRESS) && --timeout)
-               cpu_relax();
-
-       if ((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) !=
-           JRINT_ERR_HALT_COMPLETE || timeout == 0) {
-               dev_err(dev, "failed to flush job ring %d\n", jrp->ridx);
-               return -EIO;
-       }
-
-       /* initiate reset */
-       timeout = 100000;
-       wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
-       while ((rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET) && --timeout)
-               cpu_relax();
-
-       if (timeout == 0) {
-               dev_err(dev, "failed to reset job ring %d\n", jrp->ridx);
-               return -EIO;
-       }
-
-       /* unmask interrupts */
-       clrbits32(&jrp->rregs->rconfig_lo, JRCFG_IMSK);
-
-       return 0;
-}
-
 /*
  * Init JobR independent of platform property detection
  */
@@ -262,7 +382,7 @@ static int caam_jr_init(struct device *dev)
 
        /* Connect job ring interrupt handler. */
        error = request_irq(jrp->irq, caam_jr_interrupt, IRQF_SHARED,
-                           "caam-jobr", dev);
+                           dev_name(dev), dev);
        if (error) {
                dev_err(dev, "can't connect JobR %d interrupt (%d)\n",
                        jrp->ridx, jrp->irq);
@@ -318,86 +438,43 @@ static int caam_jr_init(struct device *dev)
        return 0;
 }
 
-/*
- * Shutdown JobR independent of platform property code
- */
-int caam_jr_shutdown(struct device *dev)
-{
-       struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
-       dma_addr_t inpbusaddr, outbusaddr;
-       int ret;
-
-       ret = caam_reset_hw_jr(dev);
-
-       tasklet_kill(&jrp->irqtask);
-
-       /* Release interrupt */
-       free_irq(jrp->irq, dev);
-
-       /* Free rings */
-       inpbusaddr = rd_reg64(&jrp->rregs->inpring_base);
-       outbusaddr = rd_reg64(&jrp->rregs->outring_base);
-       dma_free_coherent(dev, sizeof(dma_addr_t) * JOBR_DEPTH,
-                         jrp->inpring, inpbusaddr);
-       dma_free_coherent(dev, sizeof(struct jr_outentry) * JOBR_DEPTH,
-                         jrp->outring, outbusaddr);
-       kfree(jrp->entinfo);
-       of_device_unregister(jrp->jr_pdev);
-
-       return ret;
-}
 
 /*
- * Probe routine for each detected JobR subsystem. It assumes that
- * property detection was picked up externally.
+ * Probe routine for each detected JobR subsystem.
  */
-int caam_jr_probe(struct platform_device *pdev, struct device_node *np,
-                 int ring)
+static int caam_jr_probe(struct platform_device *pdev)
 {
-       struct device *ctrldev, *jrdev;
-       struct platform_device *jr_pdev;
-       struct caam_drv_private *ctrlpriv;
+       struct device *jrdev;
+       struct device_node *nprop;
+       struct caam_job_ring __iomem *ctrl;
        struct caam_drv_private_jr *jrpriv;
-       u32 *jroffset;
+       static int total_jobrs;
        int error;
 
-       ctrldev = &pdev->dev;
-       ctrlpriv = dev_get_drvdata(ctrldev);
-
+       jrdev = &pdev->dev;
        jrpriv = kmalloc(sizeof(struct caam_drv_private_jr),
                         GFP_KERNEL);
-       if (jrpriv == NULL) {
-               dev_err(ctrldev, "can't alloc private mem for job ring %d\n",
-                       ring);
+       if (!jrpriv)
                return -ENOMEM;
-       }
-       jrpriv->parentdev = ctrldev; /* point back to parent */
-       jrpriv->ridx = ring; /* save ring identity relative to detection */
 
-       /*
-        * Derive a pointer to the detected JobRs regs
-        * Driver has already iomapped the entire space, we just
-        * need to add in the offset to this JobR. Don't know if I
-        * like this long-term, but it'll run
-        */
-       jroffset = (u32 *)of_get_property(np, "reg", NULL);
-       jrpriv->rregs = (struct caam_job_ring __iomem *)((void *)ctrlpriv->ctrl
-                                                        + *jroffset);
+       dev_set_drvdata(jrdev, jrpriv);
 
-       /* Build a local dev for each detected queue */
-       jr_pdev = of_platform_device_create(np, NULL, ctrldev);
-       if (jr_pdev == NULL) {
-               kfree(jrpriv);
-               return -EINVAL;
+       /* save ring identity relative to detection */
+       jrpriv->ridx = total_jobrs++;
+
+       nprop = pdev->dev.of_node;
+       /* Get configuration properties from device tree */
+       /* First, get register page */
+       ctrl = of_iomap(nprop, 0);
+       if (!ctrl) {
+               dev_err(jrdev, "of_iomap() failed\n");
+               return -ENOMEM;
        }
 
-       jrpriv->jr_pdev = jr_pdev;
-       jrdev = &jr_pdev->dev;
-       dev_set_drvdata(jrdev, jrpriv);
-       ctrlpriv->jrdev[ring] = jrdev;
+       jrpriv->rregs = (struct caam_job_ring __force *)ctrl;
 
        if (sizeof(dma_addr_t) == sizeof(u64))
-               if (of_device_is_compatible(np, "fsl,sec-v5.0-job-ring"))
+               if (of_device_is_compatible(nprop, "fsl,sec-v5.0-job-ring"))
                        dma_set_mask(jrdev, DMA_BIT_MASK(40));
                else
                        dma_set_mask(jrdev, DMA_BIT_MASK(36));
@@ -405,15 +482,61 @@ int caam_jr_probe(struct platform_device *pdev, struct device_node *np,
                dma_set_mask(jrdev, DMA_BIT_MASK(32));
 
        /* Identify the interrupt */
-       jrpriv->irq = irq_of_parse_and_map(np, 0);
+       jrpriv->irq = irq_of_parse_and_map(nprop, 0);
 
        /* Now do the platform independent part */
        error = caam_jr_init(jrdev); /* now turn on hardware */
        if (error) {
-               of_device_unregister(jr_pdev);
                kfree(jrpriv);
                return error;
        }
 
-       return error;
+       jrpriv->dev = jrdev;
+       spin_lock(&driver_data.jr_alloc_lock);
+       list_add_tail(&jrpriv->list_node, &driver_data.jr_list);
+       spin_unlock(&driver_data.jr_alloc_lock);
+
+       atomic_set(&jrpriv->tfm_count, 0);
+
+       return 0;
+}
+
+static struct of_device_id caam_jr_match[] = {
+       {
+               .compatible = "fsl,sec-v4.0-job-ring",
+       },
+       {
+               .compatible = "fsl,sec4.0-job-ring",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, caam_jr_match);
+
+static struct platform_driver caam_jr_driver = {
+       .driver = {
+               .name = "caam_jr",
+               .owner = THIS_MODULE,
+               .of_match_table = caam_jr_match,
+       },
+       .probe       = caam_jr_probe,
+       .remove      = caam_jr_remove,
+};
+
+static int __init jr_driver_init(void)
+{
+       spin_lock_init(&driver_data.jr_alloc_lock);
+       INIT_LIST_HEAD(&driver_data.jr_list);
+       return platform_driver_register(&caam_jr_driver);
+}
+
+static void __exit jr_driver_exit(void)
+{
+       platform_driver_unregister(&caam_jr_driver);
 }
+
+module_init(jr_driver_init);
+module_exit(jr_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("FSL CAAM JR request backend");
+MODULE_AUTHOR("Freescale Semiconductor - NMG/STC");
index 9d8741a59037f82ea162ef2a0b894bca49b5b040..97113a6d6c58f500cd7aeb64a4d506ebfc0ca688 100644 (file)
@@ -8,12 +8,11 @@
 #define JR_H
 
 /* Prototypes for backend-level services exposed to APIs */
+struct device *caam_jr_alloc(void);
+void caam_jr_free(struct device *rdev);
 int caam_jr_enqueue(struct device *dev, u32 *desc,
                    void (*cbk)(struct device *dev, u32 *desc, u32 status,
                                void *areq),
                    void *areq);
 
-extern int caam_jr_probe(struct platform_device *pdev, struct device_node *np,
-                        int ring);
-extern int caam_jr_shutdown(struct device *dev);
 #endif /* JR_H */
index 4455396918de84320380fcca2eca01d694971114..d50174f45b21c8e0c145d7077ec86f86cb66ff37 100644 (file)
@@ -245,7 +245,7 @@ struct rngtst {
 
 /* RNG4 TRNG test registers */
 struct rng4tst {
-#define RTMCTL_PRGM 0x00010000 /* 1 -> program mode, 0 -> run mode */
+#define RTMCTL_PRGM    0x00010000      /* 1 -> program mode, 0 -> run mode */
        u32 rtmctl;             /* misc. control register */
        u32 rtscmisc;           /* statistical check misc. register */
        u32 rtpkrrng;           /* poker range register */
@@ -255,6 +255,8 @@ struct rng4tst {
        };
 #define RTSDCTL_ENT_DLY_SHIFT 16
 #define RTSDCTL_ENT_DLY_MASK (0xffff << RTSDCTL_ENT_DLY_SHIFT)
+#define RTSDCTL_ENT_DLY_MIN 1200
+#define RTSDCTL_ENT_DLY_MAX 12800
        u32 rtsdctl;            /* seed control register */
        union {
                u32 rtsblim;    /* PRGM=1: sparse bit limit register */
@@ -266,7 +268,11 @@ struct rng4tst {
                u32 rtfrqcnt;   /* PRGM=0: freq. count register */
        };
        u32 rsvd1[40];
+#define RDSTA_SKVT 0x80000000
+#define RDSTA_SKVN 0x40000000
 #define RDSTA_IF0 0x00000001
+#define RDSTA_IF1 0x00000002
+#define RDSTA_IFMASK (RDSTA_IF1 | RDSTA_IF0)
        u32 rdsta;
        u32 rsvd2[15];
 };
@@ -692,6 +698,7 @@ struct caam_deco {
        u32 jr_ctl_hi;  /* CxJRR - JobR Control Register      @800 */
        u32 jr_ctl_lo;
        u64 jr_descaddr;        /* CxDADR - JobR Descriptor Address */
+#define DECO_OP_STATUS_HI_ERR_MASK 0xF00000FF
        u32 op_status_hi;       /* DxOPSTA - DECO Operation Status */
        u32 op_status_lo;
        u32 rsvd24[2];
@@ -706,12 +713,13 @@ struct caam_deco {
        u32 rsvd29[48];
        u32 descbuf[64];        /* DxDESB - Descriptor buffer */
        u32 rscvd30[193];
+#define DESC_DBG_DECO_STAT_HOST_ERR    0x00D00000
+#define DESC_DBG_DECO_STAT_VALID       0x80000000
+#define DESC_DBG_DECO_STAT_MASK                0x00F00000
        u32 desc_dbg;           /* DxDDR - DECO Debug Register */
        u32 rsvd31[126];
 };
 
-/* DECO DBG Register Valid Bit*/
-#define DECO_DBG_VALID         0x80000000
 #define DECO_JQCR_WHL          0x20000000
 #define DECO_JQCR_FOUR         0x10000000
 
index e0037c8ee24386e941e5eed6189e9c2cd9747a3a..b12ff85f4241ece439e43310b115d16a08c5e2eb 100644 (file)
@@ -117,6 +117,21 @@ static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
        return nents;
 }
 
+/* Map SG page in kernel virtual address space and copy */
+static inline void sg_map_copy(u8 *dest, struct scatterlist *sg,
+                              int len, int offset)
+{
+       u8 *mapped_addr;
+
+       /*
+        * Page here can be user-space pinned using get_user_pages
+        * Same must be kmapped before use and kunmapped subsequently
+        */
+       mapped_addr = kmap_atomic(sg_page(sg));
+       memcpy(dest, mapped_addr + offset, len);
+       kunmap_atomic(mapped_addr);
+}
+
 /* Copy from len bytes of sg to dest, starting from beginning */
 static inline void sg_copy(u8 *dest, struct scatterlist *sg, unsigned int len)
 {
@@ -124,15 +139,15 @@ static inline void sg_copy(u8 *dest, struct scatterlist *sg, unsigned int len)
        int cpy_index = 0, next_cpy_index = current_sg->length;
 
        while (next_cpy_index < len) {
-               memcpy(dest + cpy_index, (u8 *) sg_virt(current_sg),
-                      current_sg->length);
+               sg_map_copy(dest + cpy_index, current_sg, current_sg->length,
+                           current_sg->offset);
                current_sg = scatterwalk_sg_next(current_sg);
                cpy_index = next_cpy_index;
                next_cpy_index += current_sg->length;
        }
        if (cpy_index < len)
-               memcpy(dest + cpy_index, (u8 *) sg_virt(current_sg),
-                      len - cpy_index);
+               sg_map_copy(dest + cpy_index, current_sg, len-cpy_index,
+                           current_sg->offset);
 }
 
 /* Copy sg data, from to_skip to end, to dest */
@@ -140,7 +155,7 @@ static inline void sg_copy_part(u8 *dest, struct scatterlist *sg,
                                      int to_skip, unsigned int end)
 {
        struct scatterlist *current_sg = sg;
-       int sg_index, cpy_index;
+       int sg_index, cpy_index, offset;
 
        sg_index = current_sg->length;
        while (sg_index <= to_skip) {
@@ -148,9 +163,10 @@ static inline void sg_copy_part(u8 *dest, struct scatterlist *sg,
                sg_index += current_sg->length;
        }
        cpy_index = sg_index - to_skip;
-       memcpy(dest, (u8 *) sg_virt(current_sg) +
-              current_sg->length - cpy_index, cpy_index);
-       current_sg = scatterwalk_sg_next(current_sg);
-       if (end - sg_index)
+       offset = current_sg->offset + current_sg->length - cpy_index;
+       sg_map_copy(dest, current_sg, cpy_index, offset);
+       if (end - sg_index) {
+               current_sg = scatterwalk_sg_next(current_sg);
                sg_copy(dest + cpy_index, current_sg, end - sg_index);
+       }
 }
index a8a7dd4b0d25c6741d47e40b53b9d3cfd7f101ad..247ab8048f5bea3f09e5537f103626fe84e3bce5 100644 (file)
@@ -733,12 +733,9 @@ static int dcp_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, dev);
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!r) {
-               dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
-               return -ENXIO;
-       }
-       dev->dcp_regs_base = devm_ioremap(&pdev->dev, r->start,
-                                         resource_size(r));
+       dev->dcp_regs_base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(dev->dcp_regs_base))
+               return PTR_ERR(dev->dcp_regs_base);
 
        dcp_set(dev, DCP_CTRL_SFRST, DCP_REG_CTRL);
        udelay(10);
@@ -762,7 +759,8 @@ static int dcp_probe(struct platform_device *pdev)
                return -EIO;
        }
        dev->dcp_vmi_irq = r->start;
-       ret = request_irq(dev->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp", dev);
+       ret = devm_request_irq(&pdev->dev, dev->dcp_vmi_irq, dcp_vmi_irq, 0,
+                              "dcp", dev);
        if (ret != 0) {
                dev_err(&pdev->dev, "can't request_irq (0)\n");
                return -EIO;
@@ -771,15 +769,14 @@ static int dcp_probe(struct platform_device *pdev)
        r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
        if (!r) {
                dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
-               ret = -EIO;
-               goto err_free_irq0;
+               return -EIO;
        }
        dev->dcp_irq = r->start;
-       ret = request_irq(dev->dcp_irq, dcp_irq, 0, "dcp", dev);
+       ret = devm_request_irq(&pdev->dev, dev->dcp_irq, dcp_irq, 0, "dcp",
+                              dev);
        if (ret != 0) {
                dev_err(&pdev->dev, "can't request_irq (1)\n");
-               ret = -EIO;
-               goto err_free_irq0;
+               return -EIO;
        }
 
        dev->hw_pkg[0] = dma_alloc_coherent(&pdev->dev,
@@ -788,8 +785,7 @@ static int dcp_probe(struct platform_device *pdev)
                        GFP_KERNEL);
        if (!dev->hw_pkg[0]) {
                dev_err(&pdev->dev, "Could not allocate hw descriptors\n");
-               ret = -ENOMEM;
-               goto err_free_irq1;
+               return -ENOMEM;
        }
 
        for (i = 1; i < DCP_MAX_PKG; i++) {
@@ -848,16 +844,14 @@ err_unregister:
        for (j = 0; j < i; j++)
                crypto_unregister_alg(&algs[j]);
 err_free_key_iv:
+       tasklet_kill(&dev->done_task);
+       tasklet_kill(&dev->queue_task);
        dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
                        dev->payload_base_dma);
 err_free_hw_packet:
        dma_free_coherent(&pdev->dev, DCP_MAX_PKG *
                sizeof(struct dcp_hw_packet), dev->hw_pkg[0],
                dev->hw_phys_pkg);
-err_free_irq1:
-       free_irq(dev->dcp_irq, dev);
-err_free_irq0:
-       free_irq(dev->dcp_vmi_irq, dev);
 
        return ret;
 }
@@ -868,23 +862,20 @@ static int dcp_remove(struct platform_device *pdev)
        int j;
        dev = platform_get_drvdata(pdev);
 
-       dma_free_coherent(&pdev->dev,
-                       DCP_MAX_PKG * sizeof(struct dcp_hw_packet),
-                       dev->hw_pkg[0], dev->hw_phys_pkg);
-
-       dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
-                       dev->payload_base_dma);
+       misc_deregister(&dev->dcp_bootstream_misc);
 
-       free_irq(dev->dcp_irq, dev);
-       free_irq(dev->dcp_vmi_irq, dev);
+       for (j = 0; j < ARRAY_SIZE(algs); j++)
+               crypto_unregister_alg(&algs[j]);
 
        tasklet_kill(&dev->done_task);
        tasklet_kill(&dev->queue_task);
 
-       for (j = 0; j < ARRAY_SIZE(algs); j++)
-               crypto_unregister_alg(&algs[j]);
+       dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
+                       dev->payload_base_dma);
 
-       misc_deregister(&dev->dcp_bootstream_misc);
+       dma_free_coherent(&pdev->dev,
+                       DCP_MAX_PKG * sizeof(struct dcp_hw_packet),
+                       dev->hw_pkg[0], dev->hw_phys_pkg);
 
        return 0;
 }
index 214357e12dc0b5469bb3c9daae5904ef3a618845..9dd6e01eac33050b8304c5f8758440e7286606f2 100644 (file)
@@ -1149,32 +1149,24 @@ static int aead_setkey(struct crypto_aead *tfm, const u8 *key,
                        unsigned int keylen)
 {
        struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
-       struct rtattr *rta = (struct rtattr *)key;
-       struct crypto_authenc_key_param *param;
+       struct crypto_authenc_keys keys;
 
-       if (!RTA_OK(rta, keylen))
-               goto badkey;
-       if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
-               goto badkey;
-       if (RTA_PAYLOAD(rta) < sizeof(*param))
+       if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
                goto badkey;
 
-       param = RTA_DATA(rta);
-       ctx->enckey_len = be32_to_cpu(param->enckeylen);
-
-       key += RTA_ALIGN(rta->rta_len);
-       keylen -= RTA_ALIGN(rta->rta_len);
+       if (keys.authkeylen > sizeof(ctx->authkey))
+               goto badkey;
 
-       if (keylen < ctx->enckey_len)
+       if (keys.enckeylen > sizeof(ctx->enckey))
                goto badkey;
 
-       ctx->authkey_len = keylen - ctx->enckey_len;
-       memcpy(ctx->enckey, key + ctx->authkey_len, ctx->enckey_len);
-       memcpy(ctx->authkey, key, ctx->authkey_len);
+       memcpy(ctx->authkey, keys.authkey, keys.authkeylen);
+       memcpy(ctx->enckey, keys.enckey, keys.enckeylen);
+       ctx->authkey_len = keys.authkeylen;
+       ctx->enckey_len = keys.enckeylen;
 
        return aead_setup(tfm, crypto_aead_authsize(tfm));
 badkey:
-       ctx->enckey_len = 0;
        crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
        return -EINVAL;
 }
index 3374a3ebe4c75f49ecacbec24740ead4e05011b8..8d1e6f8e9e9cf613519b14fa5e5126135d37e986 100644 (file)
@@ -907,7 +907,7 @@ static int mv_cra_hash_hmac_sha1_init(struct crypto_tfm *tfm)
        return mv_cra_hash_init(tfm, "sha1", COP_HMAC_SHA1, SHA1_BLOCK_SIZE);
 }
 
-irqreturn_t crypto_int(int irq, void *priv)
+static irqreturn_t crypto_int(int irq, void *priv)
 {
        u32 val;
 
@@ -928,7 +928,7 @@ irqreturn_t crypto_int(int irq, void *priv)
        return IRQ_HANDLED;
 }
 
-struct crypto_alg mv_aes_alg_ecb = {
+static struct crypto_alg mv_aes_alg_ecb = {
        .cra_name               = "ecb(aes)",
        .cra_driver_name        = "mv-ecb-aes",
        .cra_priority   = 300,
@@ -951,7 +951,7 @@ struct crypto_alg mv_aes_alg_ecb = {
        },
 };
 
-struct crypto_alg mv_aes_alg_cbc = {
+static struct crypto_alg mv_aes_alg_cbc = {
        .cra_name               = "cbc(aes)",
        .cra_driver_name        = "mv-cbc-aes",
        .cra_priority   = 300,
@@ -975,7 +975,7 @@ struct crypto_alg mv_aes_alg_cbc = {
        },
 };
 
-struct ahash_alg mv_sha1_alg = {
+static struct ahash_alg mv_sha1_alg = {
        .init = mv_hash_init,
        .update = mv_hash_update,
        .final = mv_hash_final,
@@ -999,7 +999,7 @@ struct ahash_alg mv_sha1_alg = {
                 }
 };
 
-struct ahash_alg mv_hmac_sha1_alg = {
+static struct ahash_alg mv_hmac_sha1_alg = {
        .init = mv_hash_init,
        .update = mv_hash_update,
        .final = mv_hash_final,
@@ -1084,7 +1084,7 @@ static int mv_probe(struct platform_device *pdev)
                goto err_unmap_sram;
        }
 
-       ret = request_irq(irq, crypto_int, IRQF_DISABLED, dev_name(&pdev->dev),
+       ret = request_irq(irq, crypto_int, 0, dev_name(&pdev->dev),
                        cp);
        if (ret)
                goto err_thread;
@@ -1187,7 +1187,7 @@ static struct platform_driver marvell_crypto = {
        .driver         = {
                .owner  = THIS_MODULE,
                .name   = "mv_crypto",
-               .of_match_table = of_match_ptr(mv_cesa_of_match_table),
+               .of_match_table = mv_cesa_of_match_table,
        },
 };
 MODULE_ALIAS("platform:mv_crypto");
index ce791c2f81f79e4ffda5d7d44e6a31a8a46bcb34..a9ccbf14096e3c03a9c193042baddc6d5b0e4925 100644 (file)
@@ -275,7 +275,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd)
        if (dd->flags & FLAGS_CBC)
                val |= AES_REG_CTRL_CBC;
        if (dd->flags & FLAGS_CTR) {
-               val |= AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_32;
+               val |= AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_128;
                mask = AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_MASK;
        }
        if (dd->flags & FLAGS_ENCRYPT)
@@ -554,7 +554,7 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd)
        return err;
 }
 
-int omap_aes_check_aligned(struct scatterlist *sg)
+static int omap_aes_check_aligned(struct scatterlist *sg)
 {
        while (sg) {
                if (!IS_ALIGNED(sg->offset, 4))
@@ -566,7 +566,7 @@ int omap_aes_check_aligned(struct scatterlist *sg)
        return 0;
 }
 
-int omap_aes_copy_sgs(struct omap_aes_dev *dd)
+static int omap_aes_copy_sgs(struct omap_aes_dev *dd)
 {
        void *buf_in, *buf_out;
        int pages;
index e28104b4aab08ce20a9c1bbf26aefdb45778379a..e45aaaf0db3069d5c99cef88664dfdc62a87d434 100644 (file)
@@ -2033,3 +2033,4 @@ module_platform_driver(omap_sham_driver);
 MODULE_DESCRIPTION("OMAP SHA1/MD5 hw acceleration support.");
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Dmitry Kasatkin");
+MODULE_ALIAS("platform:omap-sham");
index 888f7f4a6d3fa29a36c26a1ee1119428164d9df9..a6175ba6d2389f96ea118470123fd3e7818fb279 100644 (file)
@@ -495,45 +495,29 @@ static int spacc_aead_setkey(struct crypto_aead *tfm, const u8 *key,
 {
        struct spacc_aead_ctx *ctx = crypto_aead_ctx(tfm);
        struct spacc_alg *alg = to_spacc_alg(tfm->base.__crt_alg);
-       struct rtattr *rta = (void *)key;
-       struct crypto_authenc_key_param *param;
-       unsigned int authkeylen, enckeylen;
+       struct crypto_authenc_keys keys;
        int err = -EINVAL;
 
-       if (!RTA_OK(rta, keylen))
+       if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
                goto badkey;
 
-       if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
+       if (keys.enckeylen > AES_MAX_KEY_SIZE)
                goto badkey;
 
-       if (RTA_PAYLOAD(rta) < sizeof(*param))
-               goto badkey;
-
-       param = RTA_DATA(rta);
-       enckeylen = be32_to_cpu(param->enckeylen);
-
-       key += RTA_ALIGN(rta->rta_len);
-       keylen -= RTA_ALIGN(rta->rta_len);
-
-       if (keylen < enckeylen)
-               goto badkey;
-
-       authkeylen = keylen - enckeylen;
-
-       if (enckeylen > AES_MAX_KEY_SIZE)
+       if (keys.authkeylen > sizeof(ctx->hash_ctx))
                goto badkey;
 
        if ((alg->ctrl_default & SPACC_CRYPTO_ALG_MASK) ==
            SPA_CTRL_CIPH_ALG_AES)
-               err = spacc_aead_aes_setkey(tfm, key + authkeylen, enckeylen);
+               err = spacc_aead_aes_setkey(tfm, keys.enckey, keys.enckeylen);
        else
-               err = spacc_aead_des_setkey(tfm, key + authkeylen, enckeylen);
+               err = spacc_aead_des_setkey(tfm, keys.enckey, keys.enckeylen);
 
        if (err)
                goto badkey;
 
-       memcpy(ctx->hash_ctx, keyauthkeylen);
-       ctx->hash_key_len = authkeylen;
+       memcpy(ctx->hash_ctx, keys.authkey, keys.authkeylen);
+       ctx->hash_key_len = keys.authkeylen;
 
        return 0;
 
index d7bb8bac36e973944334409760dc56c37eb02be1..785a9ded7bdf3bda2840bc36daa88fe7f1532a9b 100644 (file)
@@ -1058,7 +1058,7 @@ static struct platform_driver sahara_driver = {
        .driver         = {
                .name   = SAHARA_NAME,
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(sahara_dt_ids),
+               .of_match_table = sahara_dt_ids,
        },
        .id_table = sahara_platform_ids,
 };
index 6cd0e603858321dd678b45ad74dda8f6746fba97..905de4427e7c4d1630604db45ffa2ba158983918 100644 (file)
@@ -673,39 +673,20 @@ static int aead_setkey(struct crypto_aead *authenc,
                       const u8 *key, unsigned int keylen)
 {
        struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
-       struct rtattr *rta = (void *)key;
-       struct crypto_authenc_key_param *param;
-       unsigned int authkeylen;
-       unsigned int enckeylen;
-
-       if (!RTA_OK(rta, keylen))
-               goto badkey;
+       struct crypto_authenc_keys keys;
 
-       if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
+       if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
                goto badkey;
 
-       if (RTA_PAYLOAD(rta) < sizeof(*param))
+       if (keys.authkeylen + keys.enckeylen > TALITOS_MAX_KEY_SIZE)
                goto badkey;
 
-       param = RTA_DATA(rta);
-       enckeylen = be32_to_cpu(param->enckeylen);
-
-       key += RTA_ALIGN(rta->rta_len);
-       keylen -= RTA_ALIGN(rta->rta_len);
-
-       if (keylen < enckeylen)
-               goto badkey;
+       memcpy(ctx->key, keys.authkey, keys.authkeylen);
+       memcpy(&ctx->key[keys.authkeylen], keys.enckey, keys.enckeylen);
 
-       authkeylen = keylen - enckeylen;
-
-       if (keylen > TALITOS_MAX_KEY_SIZE)
-               goto badkey;
-
-       memcpy(&ctx->key, key, keylen);
-
-       ctx->keylen = keylen;
-       ctx->enckeylen = enckeylen;
-       ctx->authkeylen = authkeylen;
+       ctx->keylen = keys.authkeylen + keys.enckeylen;
+       ctx->enckeylen = keys.enckeylen;
+       ctx->authkeylen = keys.authkeylen;
 
        return 0;
 
index 2d58da972ae279f44b91f05dd472f6287c13f081..060eecc5dbc31b24bf0c05301b37192da7e36dff 100644 (file)
@@ -27,6 +27,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/errno.h>
@@ -199,8 +201,6 @@ static void aes_workqueue_handler(struct work_struct *work);
 static DECLARE_WORK(aes_work, aes_workqueue_handler);
 static struct workqueue_struct *aes_wq;
 
-extern unsigned long long tegra_chip_uid(void);
-
 static inline u32 aes_readl(struct tegra_aes_dev *dd, u32 offset)
 {
        return readl(dd->io_base + offset);
@@ -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 {
@@ -713,13 +713,12 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed,
        struct tegra_aes_dev *dd = aes_dev;
        struct tegra_aes_ctx *ctx = &rng_ctx;
        struct tegra_aes_slot *key_slot;
-       struct timespec ts;
        int ret = 0;
-       u64 nsec, tmp[2];
+       u8 tmp[16]; /* 16 bytes = 128 bits of entropy */
        u8 *dt;
 
        if (!ctx || !dd) {
-               dev_err(dd->dev, "ctx=0x%x, dd=0x%x\n",
+               pr_err("ctx=0x%x, dd=0x%x\n",
                        (unsigned int)ctx, (unsigned int)dd);
                return -EINVAL;
        }
@@ -778,14 +777,8 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed,
        if (dd->ivlen >= (2 * DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128)) {
                dt = dd->iv + DEFAULT_RNG_BLK_SZ + AES_KEYSIZE_128;
        } else {
-               getnstimeofday(&ts);
-               nsec = timespec_to_ns(&ts);
-               do_div(nsec, 1000);
-               nsec ^= dd->ctr << 56;
-               dd->ctr++;
-               tmp[0] = nsec;
-               tmp[1] = tegra_chip_uid();
-               dt = (u8 *)tmp;
+               get_random_bytes(tmp, sizeof(tmp));
+               dt = tmp;
        }
        memcpy(dd->dt, dt, DEFAULT_RNG_BLK_SZ);
 
@@ -804,7 +797,7 @@ static int tegra_aes_cra_init(struct crypto_tfm *tfm)
        return 0;
 }
 
-void tegra_aes_cra_exit(struct crypto_tfm *tfm)
+static void tegra_aes_cra_exit(struct crypto_tfm *tfm)
 {
        struct tegra_aes_ctx *ctx =
                crypto_ablkcipher_ctx((struct crypto_ablkcipher *)tfm);
@@ -924,7 +917,7 @@ static int tegra_aes_probe(struct platform_device *pdev)
        }
 
        /* Initialize the vde clock */
-       dd->aes_clk = clk_get(dev, "vde");
+       dd->aes_clk = devm_clk_get(dev, "vde");
        if (IS_ERR(dd->aes_clk)) {
                dev_err(dev, "iclock intialization failed.\n");
                err = -ENODEV;
@@ -1033,8 +1026,6 @@ out:
        if (dd->buf_out)
                dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
                        dd->buf_out, dd->dma_buf_out);
-       if (!IS_ERR(dd->aes_clk))
-               clk_put(dd->aes_clk);
        if (aes_wq)
                destroy_workqueue(aes_wq);
        spin_lock(&list_lock);
@@ -1068,7 +1059,6 @@ static int tegra_aes_remove(struct platform_device *pdev)
                          dd->buf_in, dd->dma_buf_in);
        dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
                          dd->buf_out, dd->dma_buf_out);
-       clk_put(dd->aes_clk);
        aes_dev = NULL;
 
        return 0;
index c61a6eccf16958ff2145d1b7971d17da92aa9032..446687cc2334ed2f60a0b6f0170382fe733aae01 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 e51a9832ef0d06801fd151b376824a7ac3d697b7..16a2aa28f85672689f66c37039a8e72230e71b02 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 c787f38a186a008a6cf8fa4af1dc9d19cab8f836..e2c04dc81e2a903ea13956e872150c40ed8b00e8 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 31011d2a26fcfa0510b64c1114c1f87c05762ec3..3c6716e0b78eee2592a8c977550ce036ab4318c5 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 7c82b92f9b16f18bf1ce4e6bedb988f5d5b70e22..c29dacff66fa951f136657536a628b6743ccda30 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 b0c0c8268d42bb023ac94ca8f420e22a5a197c37..94c380f0753860c4c4002c19f4217332fa2460f0 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 9162ac80c18f303ac9a509eb97298eba33d4753b..ea806bdc12ef92418c528be0b950758de59c3ee7 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 92f796cdc6ab1dc12c6b895fb2d6e16e5dd92264..20f9a3aaf9266ea6daa71a18f08d258afa8a1a1e 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 89eb89f222846e0ff5d20cfc5e14619fc05d6600..7516be4677cf7e778ba3bef2482cae9179ff41a3 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 bef8a368c8ddcac29f4bd7b4832bd3d0fa4cbdb6..2539ea0cbc6394f918fb849ffd3c6f6ca73f33a0 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 591cd8c63abbcb081a4cd2ca264ed118f7f3d782..cb4bf682a70863e6253396eb717cc7e9ae86e453 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 61517dd0d0b73b7077be4acc5cf075792a00d86e..7086a16a55f2ed488573e475e615600c80be767e 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 f5c38791fc7466f5d683b2ee49e718d8c29d5ca6..1ffc24484d23cdb0edd1e6011605aff5c9a6eb07 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 55852c02679143f453286f189e68fd777ea1afaa..6f9ac2022abd8b3d23c739bc7face1cf662a39f3 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 c1fd504cae282491969886b0d0336784229e422d..c75679d420286c522679cdb9c344549c97e7a0c4 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 a975ebebea8aaf9b8950497eefdcaf3793d930d2..1aab8130efa1c75ae51906938447c91d12a66ddc 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 5ff6fc1819dc6a2e90c035956b23e23c56f9bb5d..1a49c777607c50d313482f3ead21c19572a1cf8d 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 54fb7b9ff9aaa4afb88c823b3a129a22440a9320..11fb877ddca9a9b0888d23952dea8fb48245b617 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 b925e1b1d139bddbc6edf86f34d4b943ebfb086c..5d3affe7e976165ec5576ac8c6551dd438af7786 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 212d584fe4272a37d947cc72b95e836d801b77b1..470292767e68e81e390e1b9065954fd2546e04e6 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 d8ececaf1b57082cc5709aca68714542657b5566..820817e97e626a498561a9e5f0f3a61f22ffc9fa 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 2c8d560e6334123097627ab59ac166a47cb1f0d6..1d051cd045dbc43b5d4b53d42008660f62765ddd 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 dd8b44a56e5d0f7090b8dd65bce87a73a60c90ef..c56137bc3868da7cdc43bf38fab53a30ca410430 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 cb9c0bc317e89ed6acebba276ed1be2788cc901e..128ca143486d1b59c0106cb8a177b5a2e1b803fd 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 a2c330f5f9521302ed04998dfb38eecb60b2f894..e26075408e9b95a365dfd188cad786593604412f 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 ff8d7827f8cbe80e2c66d78db1f50ad6fdd91b5a..dcb1e05149a7664c6e65a214d783080d540aaafd 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 d3b6358e5a27037281f6da2efb6dad3e926b9fb1..3ddacc14a7366611ffb089d21c70fb4ba3c896c5 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 536dcb8ba5fdfe69ed5f726fc6b5897f00266698..7807f0ef4e209c25ad90db9d32f7bc13955391cf 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 06b067f24c9b33f7592d65a4ece98bf99c3cb776..d0749229c875187a454c498964155c37e765d314 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 ccd13df841db790ff9eabc9cbc9df79f5f8bb9af..ead491346da70183a3a235687de975f85f89e677 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 ec3fc4fd9160e8aeddf16054cd405f35b43bfef7..2f66cf4e54fe367754378c3c8be213fe20bb8f64 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 df8b10fd1726ed466d2b05e814a8feb6c711dfe3..cdf0483b8f2dfb8f746b786fd6bf84b0d5f6657b 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 e24b5ef486b50819957d02c03f475561bccac4df..8da48c6b2a38ccd76f3e127d53e4be5ab7be425c 100644 (file)
@@ -803,218 +803,6 @@ static void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
        local_irq_restore(flags);
 }
 
-/**
- * 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 461a91ab70bb4feca82cd27c1582f81c2905bcde..ab26d46bbe1598434625979abeb488d5199992d9 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 d94ab592cc1bb21b92c851debe381609b599fa48..2e7b394def8058e4d1216ad8a07c785db4d87048 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 1069e8869f20762928ecbbe509b2ed294f82ae35..0d765c0e21ec9de8cb4e25ff66d5fd53ee78d95d 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 82d2b97ad942f96f2064c0ac58b11141fb85b54c..b8c031b7de4e045d22cfa0e7d495380bc94ed75f 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 5d4986e5f5fa6b21423084b688bd0a8afbba0c2e..73654e33f13b98c66ebce532646056ecdce79c61 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 28af214fce049db85fc02fb903a748fdbef6e0a1..4506a7b4f972319c761ff4ee459a0a38bb7139dd 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 71e8e775189e0df5568d474ea00157a2675f9260..bae6c29f5502ab951f7926bdf621977703bc96b1 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 9ee1c76da7b9110cd7e9842ba571a18b371df37e..374b57fc596d87db7e9e252c2ca4fbe57cb423f6 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 211021dfec734a5e5466e2155eaf108ee4ec997c..102674346035e13bc28d6f3f001ea0d4a5e57e29 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 89e109022d78a27cf506d995c1c16de2feffea0f..e8c9ef03495be4a450944ec996cbe85bb11156e3 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 dd370f92ace3402321bbc6f94cfd6814cbb3fb4f..2cf44b4db80c8beac0a5575cf00d1ed4cca32c4d 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 c2bd8c6a43499b74d4c29a0490a49a89a6ead12b..2f193668ebc75b7a75d4eb9ceebaa5f89a1eaba8 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 4695dd2d71fd87b6886a4947786ce9290c540127..f784de1dc7937f097265050b3336bcebf4534ef7 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 3eb32f62d72a415c30a37e040540a487bf3676d8..fd46b0bd5f2ae273cec5cad4a42b621613b307da 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 88f60c5fecbc8decfa54d4278de7a54b13c41483..8472405c558612e949e5be4caa6a6ac7c889c335 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 e5af0e3a26ec9345a9a775e77ab76b78db94ba25..0e799516a2ab998ae80c28fb8662ee0384120f9f 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 c01cf45bc3d8eaa4ac7909dd793cc35df226b5bd..3a75f4256d08ab7ce67305973b9be6d76aba19fc 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 72c927dc3be1b104c53e04bec683aa37c9c92343..54c18c220a60575d0ebc63f952937d816132f89c 100644 (file)
@@ -158,7 +158,7 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
        spin_unlock_irqrestore(&kona_gpio->lock, flags);
 
        /* return the specified bit status */
-       return !!(val & bit);
+       return !!(val & BIT(bit));
 }
 
 static int bcm_kona_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
index f7a0cc4da9502d12ef0aa602dfa249665e5a5c4a..7b37300973dbc15d1a424448d932867b4d10d06d 100644 (file)
@@ -102,7 +102,7 @@ struct msm_gpio_dev {
        DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO);
        DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
        struct irq_domain *domain;
-       unsigned int summary_irq;
+       int summary_irq;
        void __iomem *msm_tlmm_base;
 };
 
index 3c3321f94053ab68f0ab0e691afc1ddf2b83f0e9..db3129043e635c2bc9c7f6a7c3ffd111de5dc6f4 100644 (file)
@@ -79,7 +79,7 @@ struct mvebu_gpio_chip {
        spinlock_t         lock;
        void __iomem      *membase;
        void __iomem      *percpu_membase;
-       unsigned int       irqbase;
+       int                irqbase;
        struct irq_domain *domain;
        int                soc_variant;
 };
index f22f7f3e2e531592ffb313c253c50e75b73b3337..b4d42112d02d5c3388849eac299245f30f193ac8 100644 (file)
@@ -286,11 +286,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
        if (!chip->base)
                return -ENOMEM;
 
-       chip->domain = irq_domain_add_simple(adev->dev.of_node, PL061_GPIO_NR,
-                                            irq_base, &pl061_domain_ops, chip);
-       if (!chip->domain)
-               return -ENODEV;
-
        spin_lock_init(&chip->lock);
 
        chip->gc.request = pl061_gpio_request;
@@ -320,6 +315,11 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
        irq_set_chained_handler(irq, pl061_irq_handler);
        irq_set_handler_data(irq, chip);
 
+       chip->domain = irq_domain_add_simple(adev->dev.of_node, PL061_GPIO_NR,
+                                            irq_base, &pl061_domain_ops, chip);
+       if (!chip->domain)
+               return -ENODEV;
+
        for (i = 0; i < PL061_GPIO_NR; i++) {
                if (pdata) {
                        if (pdata->directions & (1 << i))
index d3f15ae93bd3bef20a5aa306fa02e1a7fe12b838..fe088a30567ac63325fd02be82b8e682aa2323eb 100644 (file)
@@ -381,7 +381,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
        if (!p->irq_domain) {
                ret = -ENXIO;
                dev_err(&pdev->dev, "cannot initialize irq domain\n");
-               goto err1;
+               goto err0;
        }
 
        if (devm_request_irq(&pdev->dev, irq->start,
index 0502b9a041a50a199b40f4d3bf956bcd81dc7fdf..da071ddbad9985670843087ee3652844958b3827 100644 (file)
@@ -132,6 +132,7 @@ static int tb10x_gpio_direction_out(struct gpio_chip *chip,
        int mask = BIT(offset);
        int val = TB10X_GPIO_DIR_OUT << offset;
 
+       tb10x_gpio_set(chip, offset, value);
        tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
 
        return 0;
index 0c7e891c8651042a8f32e5e6ecf7344cabf6493e..b97d6a6577b961d379a977e14a3c5c693cce5049 100644 (file)
@@ -354,17 +354,18 @@ static void twl_set(struct gpio_chip *chip, unsigned offset, int value)
 static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
 {
        struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
+       int ret = -EINVAL;
 
        mutex_lock(&priv->mutex);
        if (offset < TWL4030_GPIO_MAX)
-               twl4030_set_gpio_dataout(offset, value);
+               ret = twl4030_set_gpio_direction(offset, 0);
 
        priv->direction |= BIT(offset);
        mutex_unlock(&priv->mutex);
 
        twl_set(chip, offset, value);
 
-       return 0;
+       return ret;
 }
 
 static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -435,7 +436,8 @@ static int gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
 
 static int gpio_twl4030_remove(struct platform_device *pdev);
 
-static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev)
+static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev,
+                               struct twl4030_gpio_platform_data *pdata)
 {
        struct twl4030_gpio_platform_data *omap_twl_info;
 
@@ -443,6 +445,9 @@ static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev)
        if (!omap_twl_info)
                return NULL;
 
+       if (pdata)
+               *omap_twl_info = *pdata;
+
        omap_twl_info->use_leds = of_property_read_bool(dev->of_node,
                        "ti,use-leds");
 
@@ -500,7 +505,7 @@ no_irqs:
        mutex_init(&priv->mutex);
 
        if (node)
-               pdata = of_gpio_twl4030(&pdev->dev);
+               pdata = of_gpio_twl4030(&pdev->dev, pdata);
 
        if (pdata == NULL) {
                dev_err(&pdev->dev, "Platform data is missing\n");
index 1a605f2a0f55f8fac1675dd18f37dae366215edc..06fb5cf99dede237bd29f109bbd195bfa2230f5b 100644 (file)
@@ -105,3 +105,4 @@ module_platform_driver(ucb1400_gpio_driver);
 
 MODULE_DESCRIPTION("Philips UCB1400 GPIO driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ucb1400_gpio");
index 7dd4461502940952ebf6323dbb04e1af7e6a5230..ac53a95936626800143d1ad30320329f0f3ec050 100644 (file)
@@ -13,6 +13,8 @@
 #include <linux/acpi_gpio.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/driver.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/gpio.h>
@@ -1307,6 +1309,18 @@ struct gpio_chip *gpiochip_find(void *data,
 }
 EXPORT_SYMBOL_GPL(gpiochip_find);
 
+static int gpiochip_match_name(struct gpio_chip *chip, void *data)
+{
+       const char *name = data;
+
+       return !strcmp(chip->label, name);
+}
+
+static struct gpio_chip *find_chip_by_name(const char *name)
+{
+       return gpiochip_find((void *)name, gpiochip_match_name);
+}
+
 #ifdef CONFIG_PINCTRL
 
 /**
@@ -1340,8 +1354,10 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
        ret = pinctrl_get_group_pins(pctldev, pin_group,
                                        &pin_range->range.pins,
                                        &pin_range->range.npins);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(pin_range);
                return ret;
+       }
 
        pinctrl_add_gpio_range(pctldev, &pin_range->range);
 
@@ -2259,26 +2275,10 @@ void gpiod_add_table(struct gpiod_lookup *table, size_t size)
        mutex_unlock(&gpio_lookup_lock);
 }
 
-/*
- * Caller must have a acquired gpio_lookup_lock
- */
-static struct gpio_chip *find_chip_by_name(const char *name)
-{
-       struct gpio_chip *chip = NULL;
-
-       list_for_each_entry(chip, &gpio_lookup_list, list) {
-               if (chip->label == NULL)
-                       continue;
-               if (!strcmp(chip->label, name))
-                       break;
-       }
-
-       return chip;
-}
-
 #ifdef CONFIG_OF
 static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
-                                     unsigned int idx, unsigned long *flags)
+                                     unsigned int idx,
+                                     enum gpio_lookup_flags *flags)
 {
        char prop_name[32]; /* 32 is max size of property name */
        enum of_gpio_flags of_flags;
@@ -2296,20 +2296,22 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
                return desc;
 
        if (of_flags & OF_GPIO_ACTIVE_LOW)
-               *flags |= GPIOF_ACTIVE_LOW;
+               *flags |= GPIO_ACTIVE_LOW;
 
        return desc;
 }
 #else
 static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
-                                     unsigned int idx, unsigned long *flags)
+                                     unsigned int idx,
+                                     enum gpio_lookup_flags *flags)
 {
        return ERR_PTR(-ENODEV);
 }
 #endif
 
 static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
-                                       unsigned int idx, unsigned long *flags)
+                                       unsigned int idx,
+                                       enum gpio_lookup_flags *flags)
 {
        struct acpi_gpio_info info;
        struct gpio_desc *desc;
@@ -2319,13 +2321,14 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
                return desc;
 
        if (info.gpioint && info.active_low)
-               *flags |= GPIOF_ACTIVE_LOW;
+               *flags |= GPIO_ACTIVE_LOW;
 
        return desc;
 }
 
 static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
-                                   unsigned int idx, unsigned long *flags)
+                                   unsigned int idx,
+                                   enum gpio_lookup_flags *flags)
 {
        const char *dev_id = dev ? dev_name(dev) : NULL;
        struct gpio_desc *desc = ERR_PTR(-ENODEV);
@@ -2417,7 +2420,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
 {
        struct gpio_desc *desc;
        int status;
-       unsigned long flags = 0;
+       enum gpio_lookup_flags flags = 0;
 
        dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
 
@@ -2443,8 +2446,12 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
        if (status < 0)
                return ERR_PTR(status);
 
-       if (flags & GPIOF_ACTIVE_LOW)
+       if (flags & GPIO_ACTIVE_LOW)
                set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+       if (flags & GPIO_OPEN_DRAIN)
+               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+       if (flags & GPIO_OPEN_SOURCE)
+               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
 
        return desc;
 }
index 955555d6ec881faa6e821903b91f5d8c6b8c150a..f8642759116770afa976a23f12f14f1f42149be4 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 f089adfe70eed3daa21f08b81fcb6d9911ea45c6..cc08b845f9655a6b8bb516dab365da2d4c829198 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 da4a51eae824d874a2756d6dab17cdf40d78146e..8a784c460c89d8963effbb25b4509878d2003bc5 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 32e270dc714eb036a325b1d4999e18e6d7c1e11b..5137f15dba19e71a220ac38efb7e1fee2ebbbd4a 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 8492b68e873c174cda6e99e70cf976a90e5ddaff..9833a1b1acc140f36f0d248a026e43fee2b1ae33 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 7f6152d374cace41ae991b3080df769e385824b8..af0b868a9dfd6c5b3849f147f01fbf2cc29c4471 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 bf67b22723f93e1e5ab6f9b760ba8cb649229724..9864559e5fb994ec47e9b5ceb13246c9682491fe 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 138364d917824f8ad4f7f771ad7f68a6cbb35aba..953fc8aea69c141cf076dcccbc1c4ed2ec775321 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 9b0bb9184afdbfa2f1eb3ebbcad2feda23aa67a4..b6aded73838bca0fb11c5a936db40a8db1e1e180 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 f130a533a51257dd13fcbda93462dded728182d0..78e76f24343d17bad9bc81e5474f900fbd090b33 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 60685b21cc367febe98a92929fc841572858fbeb..adabc3daaa5b4f644969944a28de07921ab1895d 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 224ff965bcf7de624c3b62b6db153bda090ea4a9..a4b017b6849efd79686693e0e5cad7b5edf9ae09 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 bff2fa941f6004dca49ce176b928171d075584a8..d6cf77c472e710cf246193dca874bd3b8bc72b9e 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 c722c3b5404d3ad06836c522b9e974ab27a3ec60..01361aba033b4a39888a41bc15c4a7f5e4052152 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,37 +641,27 @@ 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,30 +810,23 @@ 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 a05087cf846d5015db76f9f7b426ee26be491376..b4b51d46f3397ed2983a4e6d3a1be796e5402443 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 89e1966271602a03e777231153d628fbe93b533e..9e978aae8972b18da9f012bef98e8ec1d408a1aa 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 fe58d0833a11c74f7b33b7ac5c169492f4a066fc..d9137e49c4e81594a992297a1dd10181006faf06 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 830f7501cb4d4f12fd914ec1bac6b0bbf7bae98d..fb7cf0e796f668328e7a923dbcada92ba8c2f1c7 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 271b42bbfb72152dc3d1e66662a981cee4a5fb6e..9081172ef0573a0eb1b17ac8e3681b0c559486cc 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 0cfb60f5476655edc097ca71c648d11544f3357e..d18b88b755c34f876dc2d4b9f0a202709dc87f5d 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 3d13ca6e257f0f2c6bd366a49f8b7f3622e592dd..0a19401aff803bcf2a1ff4dddcbd1fab42a0a187 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 e788882d9021e286624c19c261689272d5616e80..f9c7fa3d00124e9d4cd126cccd3a8f78ee0d7836 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 22d14ecbd3ec0621fd0f4d1ce61b895f224e4780..c5b929c3f77ae617d14fa87b387557bbf3cd3f7b 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,12 +374,79 @@ 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.
  *
@@ -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 49293bdc972a0d76143b0bd3b205280d4fffbd9e..4761adedad2abe5f03ae586354436f3bf1b32cbb 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 f7311162a61dc0e91cbca80f190f75c7e685d179..3d2e91c4d78e1c1003ac9521250dc4ca41c9b6ae 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 53298320080b86d7694a24a81082a2c402d2e553..7d5a152eeb0288f05e5770a9699b92e253f689e4 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 07247e2855a23f7d9b530f6383a68f0c64663a0c..dffc836144cc96266a616902aa457b46c0ef1b33 100644 (file)
@@ -302,6 +302,27 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
        return 0;
 }
 
+/**
+ * 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.
  *
index f92da0a32f0d30d02fe8afeb09f5272a16b8a060..64c34d5876ffc7c8378b3df4a389db203dd1c576 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 d752c96d609092f4de747bddef73bb19d040b7c3..f6452682141b5fd25773153233fb97983dafbc40 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 fc2adb62b7574dc9b3e1d7de810bb3074320311f..85071a1c4547921f53fd5372c2664a3066d00ef4 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 1f96cee6eee8efb3ff183de2d5a93f5fbe895af8..02679793c9e2e73d3d175136bbfb638a7cd0784f 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 f7a18c6ba4c42607a9ffb4ae2b8dd53ceeb3c3a7..fc24fee8ec833b6bdb2c26ada2b45e947622b941 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 276d470f7b3efa970c00db47bfd8eabef7c6725d..56805c39c906dfa64326dd8003e6445889f7840e 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 39d864576be4a4d5f5957cdbf1e3112b1322a018..f53d5246979c386ed632b21c08decb3ab1296f9e 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 2290b3b7383247a6dde176d71ee1e6fe18d7b4c7..c22c3097c3e857ba823cd9c339333983842c4ecf 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,10 +485,15 @@ 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);
 
+static void drm_sysfs_release(struct device *dev)
+{
+       kfree(dev);
+}
+
 /**
  * drm_sysfs_device_add - adds a class device to sysfs for a character driver
  * @dev: DRM device to be added
@@ -523,15 +505,9 @@ EXPORT_SYMBOL(drm_sysfs_hotplug_event);
  */
 int drm_sysfs_device_add(struct drm_minor *minor)
 {
-       int err;
        char *minor_str;
+       int r;
 
-       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 +515,34 @@ 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 = kzalloc(sizeof(*minor->kdev), GFP_KERNEL);
+       if (!minor->kdev) {
+               r = -ENOMEM;
+               goto error;
        }
 
+       device_initialize(minor->kdev);
+       minor->kdev->devt = MKDEV(DRM_MAJOR, minor->index);
+       minor->kdev->class = drm_class;
+       minor->kdev->type = &drm_sysfs_device_minor;
+       minor->kdev->parent = minor->dev->dev;
+       minor->kdev->release = drm_sysfs_release;
+       dev_set_drvdata(minor->kdev, minor);
+
+       r = dev_set_name(minor->kdev, minor_str, minor->index);
+       if (r < 0)
+               goto error;
+
+       r = device_add(minor->kdev);
+       if (r < 0)
+               goto error;
+
        return 0;
 
-err_out:
-       return err;
+error:
+       DRM_ERROR("device create failed %d\n", r);
+       put_device(minor->kdev);
+       return r;
 }
 
 /**
@@ -562,9 +554,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_unregister(minor->kdev);
+       minor->kdev = NULL;
 }
 
 
index 87664723b9ceada4019289a8f6508e0b0facc4f9..b179b70e7853b4bb4eb69c9a97a57295ae26a084 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 b5c5af7328df200a2ff119bafeb92664bb1738cb..93e95d7efd575fc7bf084e431ae560793e733728 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 45b6ef595965b7cb7391125d8b3a4caf2703b2b5..f227f544aa36f2104df33bbeacb5a8dc1e18bbd2 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 81192d00b39ec55f72afc68aa3ce0aab448a3e5d..b676006a95a0118951f8e12d04f8a6fff0c49fb0 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 868a14d529956545204ac34d29083c1fa60dbd10..23da72b5eae98b0bae1e400ea28d74ed5ee7e472 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 3271fd4b17240b5eca41f3cc66cecd0e2a5f7588..7bccedca487aebde1483ff08d65898e8d360c1c3 100644 (file)
@@ -383,6 +383,8 @@ out:
                                        g2d_userptr->npages,
                                        g2d_userptr->vma);
 
+       exynos_gem_put_vma(g2d_userptr->vma);
+
        if (!g2d_userptr->out_of_list)
                list_del_init(&g2d_userptr->list);
 
index 49f9cd2327575327d0b86183c1fea4e829292374..1ade191d84f4d6514fd16871cb54cbdc3208d925 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 09555afdfe9c6bf94fd798d2e899d4e46a1e8042..702ec3abe85cc88eb74aa2e7dc99b5224c175dcf 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 4400330e4449f57268efc7b961c4025795178ef7..ddaaedde173d0814b5823e628bc1f61e07eae8d8 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 1f6e2dfaaeaef7f557f4ad74ed45530dd28cd2c6..508cf99a292df18c569cc9ffd3466e8b598629e0 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 162f686c532db9cfc8dd565b24b6af1d59fa33b6..5a9a6a3063a8a603a73422f8f08b5ceaf30d8e82 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 f4eb43573cada7a7a5d2af753a8dcc4a29c898e8..f88a1815d87c4e795492ead6ef1c4c07a4d31b89 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 01dd7d225762d2b4bea5be61a46a8359f48e7081..94b3fec22c280475dfe1f08d43cf00ed1ca571d2 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 10ae8c52d06f820b5a1ea5bfb9257ca4ee2d40b7..e2db48a81ed0147738b27baaa76d663b29cc0f1a 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 62cd42e88f2866a5efc6c04d688492abd4ef4d0a..566d330aaeea272c94f168784b2e07bea6aa9554 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 54c98962b73ed43c1e7a52c2c7e2ae498e6a3621..8195e859210741cdd37ef00b0d8c6f2aa1070227 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 7a9ce000fd8678d31a7da6f6d275797ae3a36264..368a03ae3010a34d37e206c6792356799c361e24 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 1eb86c79523ef8d5b34b436d4874282561722aac..e281070611480694006a6fe3e2d629524dbb422f 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 3ece553311fe9ae030cca381e59c3ea52194fb87..5e0697862736e8372c0ac5342e3d5b5b852be930 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 697678619bd127f1acde5d62eff3ade1bd970491..23fb33f1471baf0a77f8de33f4200d7961546e8f 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 fcb4e9ff1f20ac52215c8eed147a78a843586cb4..1199180667c98af81093ff073948f2bf52aacbbc 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 4535ac7708f8a4050ed439572901c696a1db37b8..b59e6588c34327b9162c0346de826729633b20cc 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 97f8a03fee431e383800d24ca0cfca771cedda6f..c8841ac6c8f1908bf0af3a058c5d3a423a4f97ca 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 6f01cdf5e1250a7dfbefe7f00e8b213ba1c63833..07d3a9e6d79b5e5deab9a36220063eb55789ab0f 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 029eccf3013784ccb588c0bc139c917533417d9e..ba4830342d3450593bbc1f49a5a6fded4419c510 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 60e84043aa348fe3ec4293507edeb6b9477e3dc2..400b0c4a10fba3138bbb2fc4cc260058be62cffc 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 ab1892eb10740fa2b3cc46fe7cba976940b6d186..249fdff305c63b50fd72c2f62a499eb2a0d3e576 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 b8449a84a0dcab83295696322d718c49924d3f4a..41838eaa799c5afcb7510bae31118099d4d848a3 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 33a62ad80100f8912e7e56f46a06c147e5203736..312163379db9e1ecdb064d6d3cc0e8a885b726c6 100644 (file)
@@ -76,17 +76,6 @@ struct intel_dvo_dev_ops {
        int (*mode_valid)(struct intel_dvo_device *dvo,
                          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
         */
index a6f4cb5af18529308096b827bd50b68b94181f79..6ed45a984230c6db3bb8cc4b8fbdc2eec9a661d1 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 d5c784d486714d4cc5b9e1a45c8b17fe13d51871..0cab2d045135b66d0462c88d390e43eff3df7b20 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 2ad27880cd047bc93cf119784895040eeafc3bae..989be12cdd6e962e625b8376734f101ace947d1c 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 ab0f2c0a440c6a4543a59d81282c4ab21316b2cc..ccdbecca070d2340919d5499f80824ddb84db4ac 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 {
@@ -941,6 +1015,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,13 @@ 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 HAS_PC8(dev)           (IS_HASWELL(dev)) /* XXX HSW:ULX */
 
 #define INTEL_PCH_DEVICE_ID_MASK               0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE           0x3b00
@@ -1668,35 +1832,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 +1910,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 +1968,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 +2011,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 +2053,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 +2073,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 +2104,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 +2136,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 +2175,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 +2237,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 +2277,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 +2335,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 +2420,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 +2438,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 cdfb9da0e4ce944529a329ce29f92d391e19973a..12bbd5eac70db7a22a291dfc5c2e01624803c816 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,8 +4211,19 @@ 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)
@@ -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 403309c2a7d6e984c20404c38c72074f846712f7..72a3df32292f79d88d1ba17dc8d620a24ad435ca 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 91b70015585003540a35028d288c4c67f657f374..b7376533633d2cd74eeae8442a49588cf1af1cdc 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 bf345777ae9f76ec41e76c5ccf6ed3624cf4ef13..885d595e0e02255e30fbff181247b70df1af5363 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 1f7b4caefb6e0776bf61d78af519812765b37741..3620a1b0a73cbcea019cbd5503250410e51f2807 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 e15a1d90037d7709b2f4c489074ef5779ac6c1a8..d284d892ed9491e8f2a618e22576673e06887f74 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 032e9ef9c89679228a03b2d668a5bf7f68f1587f..b1390534804888e46d4db065fcb0837814743e4c 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 dae364f0028cc94d58f87613449b41d7620d7f29..79dcb8f896c6f34363e6bd62e90f1773bfcf8f0a 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 4b91228fd9bd8e50e1319816a9fe53b6f166ec68..5d1dedc02f159c8b7a21d23a940df9c5bf0c2bd2 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 ef9b35479f0136d0cb23ec7f6eb2bb59afa85e46..f9eafb6ed523a2d0f8fbd2ff981705c644733790 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 70db618989c42a3bcd3a73df44593a368e2d66d0..98790c7cccb1ab0902662e4cc541de9966f2058c 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 c8c4112de1108e9293066305eca079fdac8651d2..cef38fd320a7c5c53c687ca5b67b0d1d3192d615 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 e2c5ee6f6194eb662c4234cfdf9304c387330d9e..6e580c98dede727175cf0542b0a1024ad0e2a5da 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 43959edd4291193a538449c1d4f75bba8d30ea8a..dfff0907f70e53643093c3f6aa8d9258ab8dc5d5 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 53f2bed8bc5fabf812c16bb74a030e105bb386aa..e4fba39631a5ae2cd5901f85c73738406ef7cdc9 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;
        }
@@ -568,12 +566,150 @@ 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;
 
@@ -653,8 +790,25 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
 
        /* Default to using SSC */
        dev_priv->vbt.lvds_use_ssc = 1;
-       dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
+       /*
+        * Core/SandyBridge/IvyBridge use alternative (120MHz) reference
+        * clock for LVDS.
+        */
+       dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev,
+                       !HAS_PCH_SPLIT(dev));
        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 +899,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 e088d6f0956a87a239cf94ab31983359d0993f70..f580a2b0ddd30f5d4338c7ab61414b6fe8bc62fc 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 10d1de5bce6ff7a35921fa19d1327863b85ab86d..b5b1b9b23adf1b24559e7ffdce658ba37e6bbbab 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 b53fff84a7d5b5521e1a34c6749d1f91a5af94e4..330077bcd0bddb22ecf056766dd88cd7ed70bfaf 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,40 @@ 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;
+       }
+
+       if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp_bpp &&
+           pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) {
+               /*
+                * This is a big fat ugly hack.
+                *
+                * Some machines in UEFI boot mode provide us a VBT that has 18
+                * bpp and 1.62 GHz link bandwidth for eDP, which for reasons
+                * unknown we fail to light up. Yet the same BIOS boots up with
+                * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as
+                * max, not what it tells us to use.
+                *
+                * Note: This will still be broken if the eDP panel is not lit
+                * up by the BIOS, and thus we can't get the mode at module
+                * load.
+                */
+               DRM_DEBUG_KMS("pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n",
+                             pipe_config->pipe_bpp, dev_priv->vbt.edp_bpp);
+               dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp;
+       }
 }
 
 static void intel_ddi_destroy(struct drm_encoder *encoder)
@@ -1314,6 +1455,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 +1498,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 +1533,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 d78d33f9337d993472b22f82f3e2749d28a3e38e..7ec8b488bb1d30b6b950a23eecc833b574ab4db0 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 },
-       .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 },
+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 = 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 transcoder transcoder = pipe_config->cpu_transcoder;
+       enum pipe pipe = crtc->pipe;
 
-       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;
+       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 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);
+}
+
+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);
@@ -6175,6 +6518,9 @@ static void __hsw_disable_package_c8(struct drm_i915_private *dev_priv)
 
 void hsw_enable_package_c8(struct drm_i915_private *dev_priv)
 {
+       if (!HAS_PC8(dev_priv->dev))
+               return;
+
        mutex_lock(&dev_priv->pc8.lock);
        __hsw_enable_package_c8(dev_priv);
        mutex_unlock(&dev_priv->pc8.lock);
@@ -6182,6 +6528,9 @@ void hsw_enable_package_c8(struct drm_i915_private *dev_priv)
 
 void hsw_disable_package_c8(struct drm_i915_private *dev_priv)
 {
+       if (!HAS_PC8(dev_priv->dev))
+               return;
+
        mutex_lock(&dev_priv->pc8.lock);
        __hsw_disable_package_c8(dev_priv);
        mutex_unlock(&dev_priv->pc8.lock);
@@ -6219,6 +6568,9 @@ static void hsw_update_package_c8(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        bool allow;
 
+       if (!HAS_PC8(dev_priv->dev))
+               return;
+
        if (!i915_enable_pc8)
                return;
 
@@ -6240,38 +6592,105 @@ done:
        mutex_unlock(&dev_priv->pc8.lock);
 }
 
-static void hsw_package_c8_gpu_idle(struct drm_i915_private *dev_priv)
+static void hsw_package_c8_gpu_idle(struct drm_i915_private *dev_priv)
+{
+       if (!HAS_PC8(dev_priv->dev))
+               return;
+
+       mutex_lock(&dev_priv->pc8.lock);
+       if (!dev_priv->pc8.gpu_idle) {
+               dev_priv->pc8.gpu_idle = true;
+               __hsw_enable_package_c8(dev_priv);
+       }
+       mutex_unlock(&dev_priv->pc8.lock);
+}
+
+static void hsw_package_c8_gpu_busy(struct drm_i915_private *dev_priv)
+{
+       if (!HAS_PC8(dev_priv->dev))
+               return;
+
+       mutex_lock(&dev_priv->pc8.lock);
+       if (dev_priv->pc8.gpu_idle) {
+               dev_priv->pc8.gpu_idle = false;
+               __hsw_disable_package_c8(dev_priv);
+       }
+       mutex_unlock(&dev_priv->pc8.lock);
+}
+
+#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)
 {
-       if (!dev_priv->pc8.gpu_idle) {
-               dev_priv->pc8.gpu_idle = true;
-               hsw_enable_package_c8(dev_priv);
-       }
+       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;
 }
 
-static void hsw_package_c8_gpu_busy(struct drm_i915_private *dev_priv)
+void intel_display_set_init_power(struct drm_device *dev, bool enable)
 {
-       if (dev_priv->pc8.gpu_idle) {
-               dev_priv->pc8.gpu_idle = false;
-               hsw_disable_package_c8(dev_priv);
-       }
+       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 haswell_modeset_global_resources(struct drm_device *dev)
+static void modeset_update_power_wells(struct drm_device *dev)
 {
-       bool enable = false;
+       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 +6729,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 +6836,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 +6904,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 +6945,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 +6999,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 +7034,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 +7053,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 +7067,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 +7095,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 +7147,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)
@@ -6770,7 +7203,9 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
                intel_crtc->cursor_visible = visible;
        }
        /* and commit changes on next vblank */
+       POSTING_READ(CURCNTR(pipe));
        I915_WRITE(CURBASE(pipe), base);
+       POSTING_READ(CURBASE(pipe));
 }
 
 static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -6790,7 +7225,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;
                }
@@ -6799,7 +7234,9 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
                intel_crtc->cursor_visible = visible;
        }
        /* and commit changes on next vblank */
+       POSTING_READ(CURCNTR_IVB(pipe));
        I915_WRITE(CURBASE_IVB(pipe), base);
+       POSTING_READ(CURBASE_IVB(pipe));
 }
 
 /* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
@@ -6812,23 +7249,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 +7271,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 +7283,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 +7414,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 +7423,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 +7458,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 +7515,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 +7536,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 +7682,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 +7705,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 +7744,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 +7758,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 +7769,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 +7833,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 +7846,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 +7959,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 +8150,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 +8192,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 +8241,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 +8286,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 +8365,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 +8410,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 +8645,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 +8672,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 +8694,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 +8738,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 +8765,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 +8804,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 +8992,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 +9048,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 +9070,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 +9107,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 +9123,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 +9134,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;
 }
 
@@ -8789,14 +9271,10 @@ check_crtc_state(struct drm_device *dev)
                        enum pipe pipe;
                        if (encoder->base.crtc != &crtc->base)
                                continue;
-                       if (encoder->get_config &&
-                           encoder->get_hw_state(encoder, &pipe))
+                       if (encoder->get_hw_state(encoder, &pipe))
                                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 +9349,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 +9373,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 +9912,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 +9941,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 +9968,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 +10075,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 +10090,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 +10146,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 +10178,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 +10272,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 +10307,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 +10341,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 +10348,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 +10355,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 +10404,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 +10412,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 +10436,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 +10532,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 +10541,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 +10594,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 +10874,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 +10897,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,
@@ -10413,27 +10931,17 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                if (encoder->get_hw_state(encoder, &pipe)) {
                        crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
                        encoder->base.crtc = &crtc->base;
-                       if (encoder->get_config)
-                               encoder->get_config(encoder, &crtc->config);
+                       encoder->get_config(encoder, &crtc->config);
                } else {
                        encoder->base.crtc = NULL;
                }
 
                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 +10968,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 +11014,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 +11031,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 +11053,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 +11094,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 +11193,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 +11201,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 +11237,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 +11252,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 +11296,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 1a431377d83b76ad22bccf795db770ab3b40bd3e..0b2e842fef0151070b09af535d54fdcee2215602 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))
@@ -1695,7 +1774,7 @@ static void intel_disable_dp(struct intel_encoder *encoder)
         * ensure that we have vdd while we switch off the panel. */
        ironlake_edp_panel_vdd_on(intel_dp);
        ironlake_edp_backlight_off(intel_dp);
-       intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+       intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
        ironlake_edp_panel_off(intel_dp);
 
        /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */
@@ -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 7f2b384ac939834fe8e024df14a3e47e4e82ad18..1e49aa8f5377395c8803d5a55765199bd38af037 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 7fa7df546c1ee6e36e119af34d6e79a9d7015e82..3c77365468562ab026910a1e111fb932b288f242 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 bc2100007b21039ff628469e36e4cc48883c833a..895fcb4fbd9446bfbfafc90f95948d9a4bea5c55 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 4148cc85bf7fb303bde9ec151773480542b7f5f8..03f9ca70530c03f441ae037b16cf0c5e80b908ab 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 d1c1e0f7f2621a435ba83bd67bcca0eb878eaa43..2ca17b14b6c1c91710601dfcb34c661b45707347 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 b8af94a5be390610b360b5b27c3fe6e01d002081..c3b4da7895ed1c82b185b78ca06d00053cff2402 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 119771ff46ab5178047c018efc5f9f31a06f068d..6d69a9bad86545c6a8cfc8e8ff86480d462c2132 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 ddfd0aefe0c0906a587addfcc0d35bda414400b5..a98a990fbab3561368239ce7a7ac020c1d44f0c7 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 293564a2896a19038cc87ecba7648bce511388d2..f161ac02c4f6e613efbfc7f763cbb3566dc75248 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 26c2ea3e985c22d7011e68f3e73d14fa8e896131..caf2ee4e5441426527234453dbb43ed4c4c7273f 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(enabled)->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 */
@@ -3518,7 +3888,7 @@ static void gen6_enable_rps(struct drm_device *dev)
 
        I915_WRITE(GEN6_RC_SLEEP, 0);
        I915_WRITE(GEN6_RC1e_THRESHOLD, 1000);
-       if (INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev))
+       if (IS_IVYBRIDGE(dev))
                I915_WRITE(GEN6_RC6_THRESHOLD, 125000);
        else
                I915_WRITE(GEN6_RC6_THRESHOLD, 50000);
@@ -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 460ee1026fcad63249e83af59da0353b40b04460..b620337e6d672c164448090016a27d1b713a2a24 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 68b1ca974d594dc483827fdc0dba724563a07f8d..71a73f4fe252fdb90b4c24d8947e936b37ad0f5b 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 49482fd5b76c6cad80298d2d2f67c0050c73a93d..a583e8f718a7f0f4b87b4fe724336c73ad1a8198 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 9a0e6c5ea540544f10aa719a9c1fea6cbab82eb0..9944d8135e87f88215d5d07d54a5fd87fcf4dc60 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 ad6ec4b39005e8c6bfe6a41f65be48eaf4c7ebc6..b9fabf826f7de71f224bd9b808780f81ba1c9027 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 dd6f84bf6c220e94c7f50e1d38ca7fea9f11cecb..22cf0f4ba24871ad2697306aaec37a577496837f 100644 (file)
@@ -902,6 +902,13 @@ intel_tv_mode_valid(struct drm_connector *connector,
 }
 
 
+static void
+intel_tv_get_config(struct intel_encoder *encoder,
+                   struct intel_crtc_config *pipe_config)
+{
+       pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock;
+}
+
 static bool
 intel_tv_compute_config(struct intel_encoder *encoder,
                        struct intel_crtc_config *pipe_config)
@@ -912,7 +919,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 +1051,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 +1101,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 +1130,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 +1440,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 +1524,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 +1536,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 +1596,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;
@@ -1622,6 +1628,7 @@ intel_tv_init(struct drm_device *dev)
                         DRM_MODE_ENCODER_TVDAC);
 
        intel_encoder->compute_config = intel_tv_compute_config;
+       intel_encoder->get_config = intel_tv_get_config;
        intel_encoder->mode_set = intel_tv_mode_set;
        intel_encoder->enable = intel_enable_tv;
        intel_encoder->disable = intel_disable_tv;
index 8649f1c36b007f89ea5b2bbf8bf3bb26090d9f52..0b02078a0b848c4127385b1d3b5d3ab6be755c80 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,61 +205,16 @@ 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)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       if (HAS_FPGA_DBG_UNCLAIMED(dev))
-               __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
-}
-
-void intel_uncore_init(struct drm_device *dev)
+static void gen6_force_wake_work(struct work_struct *work)
 {
-       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);
+       struct drm_i915_private *dev_priv =
+               container_of(work, typeof(*dev_priv), uncore.force_wake_work.work);
+       unsigned long irqflags;
 
-               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;
-       }
+       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);
 }
 
 static void intel_uncore_forcewake_reset(struct drm_device *dev)
@@ -274,12 +230,49 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev)
        }
 }
 
+void intel_uncore_early_sanitize(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (HAS_FPGA_DBG_UNCLAIMED(dev))
+               __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
+
+       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);
+       }
+
+       intel_uncore_forcewake_reset(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 +285,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 +301,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 +330,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 +340,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 +386,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 +461,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 +687,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 +788,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 cc3166dd445ac8f8371752a2ab3f1b13bf4811cd..087db33f6cff19f8cdaa32f390c6c4dd52a8c79d 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 598c281def0a34ef1e50a04af7dc9f06b54b04c0..2b0ceb8dc11b8dce6894cf056998714603087a3d 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 b487cdec5ee792b6d00fb2a086202426c6a94f99..3a1c5fbae54a5486252e91dd4ae1c0f9fdf95a59 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 fcce7b2f80110d2c9a2907b02210edc141f1aab9..f15ea3c4a90af16db633d9082d57f2344fb4565a 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 baaae19332e2e9d98d3cda327d44b8f7c5d5ecb4..cf11ee68a6d92bf8e982e3063d86befcaf2ced6d 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 0f8b861b10b3e6749704ea6a82cd57457602bfc6..b1120cb1db6d76b76fd38afd3279a3684e6b188b 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 503a414cbdad2ef83db8f10c9eaadd80522c515d..ee6ed633b7b1c1bb34ac6b59ec85db52c69a4f27 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 a06c19cc56f8988f7399641b9d9048308773f875..f39ab7554fc992175630831acf28bbf223cd9f50 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 e17914889e545eb20f008450846c582e15aae211..e5fa12b0d21eca645a2cc5efa8f74bef595ad2bb 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 35463864b959962e362175e75796582c2fc6d1b3..9588098741b5b7f76e379d715eb358bfa78d92f0 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 d183516067b4a08856c0e8d41b687906e734b364..d4afdf6575597f0dd4d06ed2eac5fc09b2cad076 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 61979d458ac0850d4bc54839c0e1d9ee0bfd3c84..33dcc606c7c556a55a8e463d87bf56a0368c6363 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 94c13f418e758de1a58b1e008404e73ab2788942..259ad709b0cc0467d1a4e6a55435e63ea4cb3415 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 6f8396be431d64a9f95b5ff5c27d211e6688ea7a..6d4c62bf70dc482cf9f38ea1739046cb19732943 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 aefc1b8feae9caff6100fc879c6c2180f0f18be8..d1df38bf574783bf98c39253315e3810527d1377 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 a225e8170b2ae9d9db46f984a4377d36d4c33a92..0030a111302dae44c6a4ecc0e21d5aa9e7364032 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 f5fa4865e059dfb5c00d5cead1845e52e46817da..4e939f82918c68daee08cc95253eeb25f31c24b8 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 bee36363bcd0020ece46447aa0bd4dcfa8748538..dbde4f6339b9f82b257c2d3ba24cc534d066760e 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 bbeeebe2db5552d57e5b9ad525fd18b935a659cc..9908ffe1c3ad093f7f73bdac19cd34a9abc6e450 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 de6bea297cda8e22b8a896439b6c8f4570f0b9b4..019d530187ff2c52a0523790047b216d51baed16 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 7b645f2e837a946621e6593ada88535f7acb7a72..17330b0927b2a0ac0f608b2e9a98777cb96016ec 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 bc7fd11ad8be4e7d4785d0f696d128220f25b7ca..8972ac35a43dab9f1006641e69734d1c9c0c40d4 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 1e83554955f383e7e866e397cb7c31bff6ffc4c2..eb015c834087c00189f9afcbe843ae1b0f0a14db 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 3468229d58b31daae10d64809f8b6f0840ac0d15..0f0af243f6fc41cb030f40985cf3466aed945aa6 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 b3a2f16290417cb055a8d7d89dc8f3f876b3a090..86537692e45c4efaeae162d32753cf5613216954 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 df8f1d084bc1d76d1ee8dc5db948e7ebf9f87771..d39f0862b19ebe8db2a74fdcedbb8a52086df3a7 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 2bae46c66a30dd4c3eac623c07dbf55c70d32cb8..e587d251c5900cd4532cdbff584d0683131616ed 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 0676f32e2c6ab917fe9980380c82137a1ad8f7ce..f4f23a578d9dc76fe4b7d03498729b099471694a 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 3bab937965d1f596405864676464fe6370bf87bd..4583d61556f5bed5c7d9cf9d26988d1160ddb386 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 ff80f12480ea2223ec85583cd47b01f4064a2fd8..7cf787d697b111aade40bf0cb34a1db52a2c41a6 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 d939a1da32036c8c9b37026d2cd2ef8f8e1bd0f7..edcf801613e66ea5c34665eafe1147d39cda62db 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 7eb81c1b6fabf9e58444f3f4a7c254ef01436bab..3f3c76581a9e3047f3716943f22c489ffea0c946 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 62a432ea39e5b2d6b8cadfddee26cb39e7bc94e1..9f6fcc5f66c2e375b45ebf5c58581b0e867d9765 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 52fb2aa129e8433e98d6800ae10a9e5bca191896..03e0060b13da6f65701531a514e0c7c0cc1340b5 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 4c72571655adcdadc2067f768abd5ad6a3cdeae5..9135b25a29d09a4e82e57e1c9a1a27608eee6a99 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 a0284cf09c0f724a1b963287382992035a75bbfc..dbd2dde7b7e7649d2873c513d69a263b59329b77 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 1b7809a095c3b51b24dc5ba3d40c4adced824a1e..6e03dd6abeea51da34fe9e1046e50075fc481070 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 12a4005fa619ce0412deca5e393378c01322f9bd..dcde53b9f07f7cd77f2886b8c8b927054691f289 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 cef0f1ea4c217a1ae430b6dfde0fdf66459bf948..7b8662ef4f59193a91246ad074c8e87d84bb9019 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 1719cb0ee595ed701da589cde8e446b379340886..c8c41e93695ee3d0f83da140f0615cb737c14472 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 ffc18b80c5d9304db78ff3e4978b5fd892b14d99..db139827047cf4a6b103a2d98cc7fe5c208721db 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 418f51f50d7ad4768d480fc3f0de59a657a025ee..8d06eef2b9ee02f7b3d895c8d7a37bfd6e022924 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 7aca1877add4260d279bccf90424a52e6a9431f9..3900104976fc7bfc2ca3df441318254bdae3bf97 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;
@@ -174,6 +188,43 @@ 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;
+#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:
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 054d9cff4f533596e0eb52ef4ad5497ce727ca82..1bd4c63369c16f96fd30762c1faaddb60acdf337 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 05e903f08a3696d0c101e67700be6dc24ebe4a92..a0bc8a89b69941b1bcf449084ce1dcea9a2ff666 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 52dd7a1db729fdf086607cf703a210ec9aeba125..378a015091d2bf22d9a2ba5e49cd095b3ac774c7 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 7ec4ee83fb641463383402742f85ebdf2368ca0d..eea3ef59693d6dd3158764690f3e82b8286ac022 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 9e1d435d72820c3c4d7825fdd9b40ee787b21aaf..d2df572f16a3afda530f60a049daed4fac0bfbac 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 f877bd524a922a81389a9e646a86441cb52b47c5..54f26cc801c722010857f30d745dde967ba968cb 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 2c927c1d173bdb9cfae210f57256af68d6f2e75f..571a22aa1ae5dddccb39df930c133027bd267644 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 a9cb51d38c5784bf6c55679ac21e600075d25263..f257602093160a47d7a30acf12f230db0b4c668b 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 5c7433d5069fb390db11f0a03b2f5d72b54ea787..343487ed2238a1139f2a0bce92bfcbd2a961335a 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 7e5dff51d3c553a4dbad93b0e56dd7e8ab7c78f7..5f555788121c9ff8c63a7e66da794011b67f237e 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 91a87cd7195a79e478549c5f5f9c701ef1a8dfaf..0908dc834c84c48ac34f91afde1984ed593531bd 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 ce92f289e7514aaede4d6aa3c9dc15312cc59410..9ac94d4e5646d2cd24579f3b47777f2e17f5e9fe 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 8e8121abe31b5b9153ce745ed5b076feb811f3a9..04f412922d2d43f5a5fbce11185fc2f9f4196e7e 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 64dca260912ff645c8dce1a338c56cb9ceadd1ef..fe67415c3e175ce17de2be2e8ba7e03570d97cf7 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 e5be3ee7f172e49b079befb0736c1358b622cc97..71b4283f7fad532fa7a799fd9182f4733e4bb7ce 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 438e784108082956fd72343d60b8f0482ff20a56..c4740d52853286eb5dc75b43710c3c4eb0c07d3e 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 818a4751df461f8cafe852daf75d27eb367fb048..a1102cbf2fdca0f95e30c2d0ae77d9c3ddbfd72d 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 23c143aaa55640cb5b3a50f7862e38950d8427b7..4532f7e5618cf9f8d96d21198f35dc573f78b028 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 3f4f35cc3848ae6bf08e3d7164fc360366ed5f7b..434bb4b0fa2e650a130a98cc940f5ca7727cfeac 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 c19004301309ea0c039fe8336d7fffdb906b24e7..7eb6d94c84e2ac10e3afc72f8db201504b47a8e1 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 dd6196072e9c7f58062c7e500ea8c57ba19cb580..d4e7ec0ba68ca66a8c3600df9532a33c43bfbc11 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 2a859a31c30d23c8c6d1c4989af56b75fa810b1d..c571758e4a27c1b3b8c140c833ff24754504b225 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 a019364b1e133382671a7abe37349da64ba30190..a62f11a78430e3949e057d77000d0776b63e35e9 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 c48e74953771a6c02352d43c0bfa59a84be13c0e..b574dd4bb8285e65ccdbcf23db7b6f139e1cb73c 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 d698e710ddd43cf9cf5a524201d0e8ba5c2e98e1..f9430c1bf3e511a343373f2c20bac059dd2f7b82 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 5a5961b6a6a3b28f3775b035c03d6f6f74af25d5..560c3593dae75e365a647d3e4858d65be85c39e6 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 9ea18dfcb4d017a5540bf170c8c574632b22e0c2..8092e2e9032377182563296182edee953eeb1c39 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 99b6600fe80abada73eeab576a2360a3ec3b68b5..ac2881d1776ac1a071371a84f379ea57ea8ee856 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 9e094408f14e1f6b87600efaccb6cf434124abdc..5d539ebff3ed3c48d5280820e5875c434d5dcea8 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 27074957fd216bd25dbcfe6c8afe3670cff5c9c2..ed055847887e4a8fcddfbc5319d7a9f752b5bdf9 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 d87836e3a704552c7251f6f2969f2df1f3d9fa0d..0f9a37bd32b079138ef1122ea749ca55d5b673a7 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 633c2f80648264ca49020a4ef5b593291d5d7120..8c32cf4d83c78bb39773cd11233e59941da52699 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 1d1a89a06ee40a49a4ccda7b2af62f92f27f23e7..9b0d938199f6e888fade2ea617ca58a42a499fa0 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 45799487e573e226856247d21087f18ccfbf09c6..23a462b50d030d7a1f4d3e846959301c7e1b358d 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 96d3364f6db30bca72ac8c9b9947c5230b3eb678..c7b2e586be0b3e282643b57f84586a5884fdb62e 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 0b285e99be5a18dad1aa8119a94eb5c54018ec9d..16ff06ec2a883b83b503e9177db08bed2d199d38 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 7d88ec4a6d061c3dddce107af61608355c2cf08d..697f7ce70aabd6b3ee6c47cb45fb737ae9d36151 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 89ee289097a6533a66ea50a85c298d4163378d8c..e2675bc0edba5f9baefc5d514750d151a2ce23a7 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 2e740508426108ff7fdc5c9421ddaeb7a18f063e..8541aa382ff224136d8c1cedff00db0595a753c2 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 7e4e2775f249652459c83bd66d89eeb50f2899c2..9fa5da72387192467ba80c9f9f9a7f2a0adbe339 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 ce6569f365a7c133ca7038bcfe4827ab79d55770..adc88b73d9118e4b7d50ed21209675fbc47a8820 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 c075998d82e6605272fd40a6be1d3e1e1d221b8f..69891d4a3fe7e3f50a840e4cdc2a34196bcd0010 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 663853bcca8288f9e45f944c950b672d219ecfd0..7628fe7592206b5b33da955bb0115aaa7fe5ca14 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 57cda2a1437b0d2076d0661d5202ec73337e0ad8..420908cb82b613bbc97fbccb9b123f3449926100 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 bcbb056c2887f1ef603dda429b3bcaddb78ccc85..675e221680aaa008b085259c1c63f42a6a954542 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 f835501203e552abb29b68425cb5b0a9b37e6bec..1f76de597d4bf17ea81c683c67a716f824ece15e 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 8c7f8057a1858b5f97f4a604b9d538892a30d528..23921b5351dba6639f02b7dafaa552946b72b13a 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 34132aef34e1d12bdd2527f92b48a3449c33efef..94da46f61627d044ef4a9ae61c940c8ca687c22c 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 f5b2117fa8c6341f3797f258eeb2fa0008394cda..11918f7e2aca1cffbd01af23263ca136014ea5d3 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 b192d624636399b34d3d4765872db5e1127061d6..73839d7151a7a94b502491475f289cdce5594e60 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 a1427758659532babd63689c03584112e973b4c5..da50c1b129283923f841eef8c4192df09131f935 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 0db5dbfd91b5b0ef6cfb2209432ca92714fa7300..db7346f79080a2345c7a08c943ee934b4055a569 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 d09d3e78040c13630d3e0e677e05535c3cfd1c25..250a6d96016bff563946563e93d2aec4a3eeba13 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 f074cd20bc9c5f5e51c403f51f20b340609d832d..4f5a1373f002b9c09e55f36e8a0acd24fd18ec46 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 439d81c261309bda696d9e6e51a1e4b122c3b6c6..c3105720ed246c08064c53c92d73db898366bdf7 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 cf1ed0dc9bc987d7c7b9436f80aebd22236eb793..b47d543ab2e38c365396d55aa1d8d08f80567510 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 2fe1f712eefa977bd69bba0ea05e15f939dd7f16..8eca457c28146fe9b271072d13fdfc507cb10a39 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 b22357d9b82185023b9ba93bb70a76ca6f99ae84..27c8235f1a85d08a3128dd3266416461ba08a9a7 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 463b08fa09688e69da4f5e3ea640412b151c222a..8d274dba1ef17a363af4d4c33c3cea4451025159 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 821cd75b86a3c4f2e313bd7448929573ac81faa2..f009d8a39d9d70f4063ee8df300ba399682c093f 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 1f103c7b89fabcee0412b19ba78524a63c02ef82..8309fe33fe847ed2f418cd13bbaad2dfd2b2de10 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 be069b5306b6b2550b3207635233629a431829e3..ffb7ec6d97aa0bf2c13ae9b914464e24f59aa4a4 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 57a2af0079b3126e6d29af4dfaa9bbdbbe4cadb1..9159a5ccee930d21172884bf9f23972c222669b5 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 b18c4e63bb475cbc921eb02dc1400c8e0f3cbc6c..f003c1b1893f48c1a115e55e835a749d98b41207 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 32ccabf10c45e700320240805a51b39a0cdd7765..f34f4223210b8552bc143f096bd8117ca216e158 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 bef756d43d3306780bfde232c4c9ede78470ef2d..69093f7151f01d168117098983a6812e9b3ff965 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 097d8e3824f28b035620b6f1770096d274d30191..161b06e8fc3f75f31567054c3f68117e0691e93e 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 9d6d9df896d945b3f247f27da3703c64d10fce17..2dd3d0aab6bb6434fdb780c1152f71453aa22324 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 33b4393a782954d086b48e7f191d898ff652a3c0..95a115ab0c860bc591f63033f6305c7f15fb0b59 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 02cd83789cd432182a8de50c6095da0186de1e90..b239a8615599d92b22478295c6556a6bea86cd46 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 c5246c29f2934b895890c42e928594796bb07bde..d8478208a68117cc83ea66467905f98fed9474d7 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 e2b57909bfca3c0f8e773f1152ec3e9ce6a37ae2..a5b77514d35b7c4caa91660035e91b6b64c98b17 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 fe6a2278621db54c3b221a58f13a2cd86422de53..3bea142376bc2c4cce39aa685b266156e259fe37 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 5eca99b8c7e2685b00aa9402c68e123bceb9cf78..666cbd5d47f5313173ebc8e31e2b14472a337fc7 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 1190b78a1e91cebf82ae644b7c72e8e6c6002e2a..42e64f364ec1787cd430362023567ff7353900a0 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 da614ec5564beb8acc949bb5d520acd8c4811d8f..cbc7f00c1278905bcf24b2c7f8dc548c6dce7a22 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 f35d76fd746d802a0879b61199259b5b1d43b411..e5fc37c4caac841977a99e4530059ebd6235b36f 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 db9d6ddde52c31cc7395de7cca70ceaf0671d015..493125214e88696db12ff1786f88be701e108225 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 ee49ac4dbdb65c7d60bc74a828837a86f009edc5..7648beb1119900eb162fe591f4990be8bba21410 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 1dab7e12ababcafa33d72411b3070c51065a2003..d64498a4d9ee28c23102da40b290090a9053564d 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 25fff842e5c1ebc8745ccb59fcc3e4ca10336f2e..089acac810c5df92ba5045c7c7b9a98b210d33dc 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 ab7ef0ac9e34c121e863c8110d243557773c681f..baa013afa57bb6ffce5ccccb21ff91f671af777b 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 903baff77fddcc20e8e2f18a6df808d66c0d316d..76762a17d89cfe46a7b794e510fb83225c4aced7 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 cf97c4de4a6b2911722da3b1799487f217cfb560..f464547c6bab70c714626672bfbb015d7512d1a3 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 d422acc9af156bc0ab2cdea474d37d0691e4e9ba..f572c2804c325f01896c061988626eb91b8e2f40 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 2895c19bb1529f14196a4ff36d6861af57c35dd4..041fd5edaebf7de988206ec6440bdf1950edc938 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 e290cfa4acee09bd8f0a1a2b54462e38abf0bcec..b4b9943773bcfa710580244d43daf69a13d5043b 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 64aa4edb0d9d958d60daf543fcd7d98209da10fb..2d787e4dfefae71ca18b341dd1cd0bc72c0b1fdf 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 d9891782bf28ea69a906a50714110914af9cb8ba..3bfee5c6c4f21e3bd25430bb693e5d634c86773a 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 2b1afe225db84b6a715e3891a96e649f65a7c380..e8822a934c485742f9b2e8452225ec93ddec5f07 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 06710419a59b3c9770f0de874e022b9b4effc4a1..f8a6f18e2d3408f55b1447e55144e24ded4036f9 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 104175c5a2ddf0a08ca82a01246b551605397246..c02b4763a2d50e40db65cf2a8a247f2e8374cd3f 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 e286e132c7e7d744d88d99522cf2ce733b4899a6..129120473f6c67bb0d90fcc366e3f140c558f79d 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 f1de7a9c572be624d607d450081499a447d919ca..80e584a1bd1cf4e4d6a32a8e3cbfd79739acd1e3 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 39f47b950ad1bd79a1315fb9a4a9bd725624ed8e..95f6129eeede9e39f116844cefc8e324eced20fc 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 e601773ee475248635856ad035393e4793ee7a82..f69dab11f720110a4af35229db663e41f58623cd 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 8b3adec5fbb19e8a34cdb53fa83420f7b57f21b2..e44ed7b93c6d88db3826a6c6dcb13efd5429385a 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 42ba633ccff7f4bdf382d6f283b165c067f4157b..1d15c52fad0c3030e4a2c103a2a69b3529aa1b19 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 d11a7c400813d9e4aba4a95e7497c1faefe21b94..3b2c4580098bafea567ebadd0cfdb972adeadf93 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 54c28bdc42048d2bb9c6c2218833811c8cd8b5f5..4dd4f81ae873d32c6aa47ae1c64d65fd252cae11 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 dd38529262fb7622dccf9d825d84bce7489b8d8a..96f8f95693ce10654a1a4339a1e0eb16dba6848e 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 b80a33011b9313f8ac4e771cb93aaaf7e163d889..cfde9eb44ad0142309970f2eac404c8c5b368c69 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 57711ecb566c2c7b88c7a7915f6efe56b6b924fd..c0bdd10358d76b748d4fc22e3ac480c99760cadd 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 ea3f5b8a0f95d7813fb9b78cb95b0b257e7b4cb4..424a489d0f039178d2c8e3f60249790f700f65cd 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 2e70462883e89efe2be217c37f85d1754d5426a3..2a15b98b4d2b826e7fa45ddb7c4611d940206bd2 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 d4fbf11360febee34d2774c08233944c6b546fc4..0e3270c3ffd2296caad7cdbcbaadf9fdc7044be8 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 93dd23ff00931c473af05dd8280aa6e2fb40369e..936a71c5908076814a4aa8ffc5c2a3757e745ea5 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 4908d3fd048611a048051cbb5d5c27d56c6f9b60..b13ff0fc42de4b2dcaf175e4a1ec3a126f04732c 100644 (file)
@@ -140,6 +140,8 @@ nv04_display_create(struct drm_device *dev)
                func->save(encoder);
        }
 
+       nouveau_overlay_init(dev);
+
        return 0;
 }
 
index 9928187f0a7d0bebdf3c80f8aec00809b294546e..56a28db040004fe6ecfebb6b3b69c8feda6a85f5 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 973056b86207d89f191e8e37c872d7f7d66ca9b2..aca76af115b37eb5da4ebafd76d3343c4dc851ee 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 bf13db4e86317191fcda44ada046dc35c1fad161..cc4b208ce546d50aad1813810981099fe6d81935 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 8f467e7bfd196f70a17e892775642aaa7223238e..6828d81ed7b99daea875ffd06c4dd399d4aa05dd 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 cfbeee607b3ace2eb0c602919033b570580f2625..95c740454049ad1b4a4cf23c2bc63d7038c7a362 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 6e7a55f93a85207c10198b890b31df7dd0f7bd3c..2953c4e91e1ae9fea8fff60501dfa863d56c63ea 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
@@ -23,12 +41,28 @@ get_agp_mode(struct nouveau_drm *drm, unsigned long mode)
        if (device->chipset == 0x18)
                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 2ffad2176b7fc7a2e2f6a7ee90d653286e4151bc..630f6e84fc01989da5f3a8957bab9d450f888c92 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 3e7287675ecf627fb3e6db850548e363e7ad2772..4c3feaaa10375721627b4bb00c5f95f0e6eb8a9a 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 755c38d0627171e985ad87abe703b3407081648f..c0fde6b9393cb24fa602e838fdf145657d059d05 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 653dbbbd4fa1d48ca43092bc79ad0970f90c7665..ff17c1f432fc9e9312ace42491385bb76226b381 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 e84f4c32331bf291f7cd3eeb3f253cd8fd543f37..cc5152be2cf121a0901d323bf41b57886b56b27a 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 c5b36f9e9a10d7ce02d68197181f427b950732b6..1674882d60d5e842547e420f1baf48a13d534e66 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 6e399aad491a0b284a059d2292c9092716a489d1..264a778f473b90a76c1cc3909c778dde9a736cd3 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 7848590f5568e4142457a0f121d9282ca6d1ae17..7809d92183c4236c9f1f937ea6cde6a39e780063 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 025c66f8e0ed0b610693e9f023e7afc12fcb02db..8bc8bab90e8d34462f0b484a90710a9094247216 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 690d5930ce32225a382a472ebc90ac4b0fdb972b..984004d66a6d313d1934230bc654c091a77b5f8f 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 e893c53624024751930f51c28517546d7fc7cb9d..7a3759f1c41a67bac6b48cdcfd5b10b5ac30ae39 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 994fd6ec373bae1e21e1ec61ad3a61cb13c5cc9d..4b0fb6c66be918857bc6529b3a7c5a7c7a49a498 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 a86ecf65c1642b2372e9a0e0136a69c7b068e041..7903e0ed3c75c74fa2d2b46a71ee01a50289de68 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 be3149932c2d4c3035a36cf40e68f04af8ffd808..40cf52e6d6d21ffb818f70b2b01be3b5f5e02b95 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 f32b71238c03c3426034004779481925c8dc00ec..78a27f8ad7d97be653a63506bb60bd9c7b518252 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 502e4290aa8fdee0ef200410f4fb0264ac82aaf8..7caca057bc382dbae3d6e849d26ad4cdf103e0b4 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 936b442a6ab7f3f2e09ecea341e3105df51ed630..4aff04fa483c5e82acf1c66b1e63833bc23391cf 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,36 @@ 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;
        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 e90468d5e5c0aed0c39a7fb9bd2465909f0ee554..51a2cb102b441f14f49bd0e9b663e15aaf078592 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 77dcc9c50777377945cf6cee1867ab12c0a7cc8e..8fe32bbed99a8352eec8cef3a53bf77d0fc07662 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 20c41e73d448f4e455cd1a85f42697d0b046f43c..6c220cd3497a26ada576caed6d51f5d001e63839 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 2603d909f49ce032f1933a5d8d17131de132c0d9..e7fa3cd9674389e1cac2de91816ce6b0013b2a1b 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 30b95b736658b0c9154ca54c289d99e19361c16c..07847693cf494caababe5780bae717ef393c8af1 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 533f6ebec531ff67a1c1f7a96c2504f852d665f0..5aec3e81fe241e8ef1308d1b53feca75b3dfa34f 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 9263db117ff8ae937dbf743ac0e8a267264d95a4..cb858600185f8051c7f92997e5e7e8ad7d8a6173 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 d6c12796023cd654e95196bc9d55ce18aabdb114..037d324bf58f6ccfda14ff471f3c98b4405981a0 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 835caba026d399d4e3750fbc3b47d44c7c6b7df5..5e827c29d1948a658cf61205b7607ff5a53263d7 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 514118ae72d4671474b165c5c0b7385f77b04ae7..fee8748bdca52fda145888018b3d4a89c43de866 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 f7c9adde46a0c8ffb5586eb44461e2c3155e88cb..7bda32f68d3b50e528c21c3bba29a3b2018d1df1 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 88722f233430a3d0f7eb4e8b35ad929b709c5b35..f437b30ce6896736da893c2a8651ca23e0a1ed55 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 1648e4125af7619f9923901306935b0f152ac0fa..b96f0c9d89b2d717b552d59c19e26d4828214f66 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 9e8da9ee97314c9519f5ff29f03fe7d6dee18673..e5ca498be920a86be9507c94858637aeb0b3c4c6 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 0109a9644cb29ef7a6e13ac76a90544effcefd20..821ab7b9409bb866c61d793c4e0ab9d63d73ec98 100644 (file)
@@ -92,6 +92,7 @@ qxl_release_free(struct qxl_device *qdev,
                                                - DRM_FILE_OFFSET);
                qxl_fence_remove_release(&bo->fence, release->id);
                qxl_bo_unref(&bo);
+               kfree(entry);
        }
        spin_lock(&qdev->release_idr_lock);
        idr_remove(&qdev->release_idr, release->id);
index 037786d7c1dc882d89d73d544460182fdf0df0cf..c7e7e6590c2baeb29485e6a9f31b44f0631fc606 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 af10f8571d87b076c94eadc67a13aaad206a3918..92be50c39ffd4a4b498d439093725e991bead162 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 bf87f6d435f81ed4f482bbc99dfeb220fdc6d8dd..80a20120e6253590d07e9ec4b0085b6ea9d31437 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 00885417ffffd2e8ad695a1a01059d40df68c011..fb3ae07a14692601a912b1cbea37ae7ff13bcc62 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 5e891b226acf9cf60db309fc8b8c596660fe1d96..a42d61571f49fa877e6c42564b7edeb9b1e2ef5b 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 deaf98cdca3acb80300b889b61ddf5a7f83d20b8..0652ee0a20989db0b69f816d45e17f9354e5ca8a 100644 (file)
@@ -56,8 +56,10 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
                        return -EINVAL;
                }
                args.ucRegIndex = buf[0];
-               if (num > 1)
-                       memcpy(&out, &buf[1], num - 1);
+               if (num > 1) {
+                       num--;
+                       memcpy(&out, &buf[1], num);
+               }
                args.lpI2CDataOut = cpu_to_le16(out);
        } else {
                if (num > ATOM_MAX_HW_I2C_READ) {
index 51e947a97edf945d02594dc0731b098ac033e1e0..1ed47997635803d9e69bba8b3bd0202ecbc04b0e 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 252e10a41cf5a065872ee50597a7e5f2b2e3035d..9c745dd22438953106715feceb974cb620c7db02 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 9cd2bc989ac713d1604cec5ab1311ac3680066c5..b43a3a3c90671911a4eaa0a7c5a260c42118df41 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;
        }
@@ -1378,17 +1560,17 @@ u32 cik_get_xclk(struct radeon_device *rdev)
  * cik_mm_rdoorbell - read a doorbell dword
  *
  * @rdev: radeon_device pointer
- * @offset: byte offset into the aperture
+ * @index: doorbell index
  *
  * Returns the value in the doorbell aperture at the
- * requested offset (CIK).
+ * requested doorbell index (CIK).
  */
-u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset)
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 index)
 {
-       if (offset < rdev->doorbell.size) {
-               return readl(((void __iomem *)rdev->doorbell.ptr) + offset);
+       if (index < rdev->doorbell.num_doorbells) {
+               return readl(rdev->doorbell.ptr + index);
        } else {
-               DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", offset);
+               DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", index);
                return 0;
        }
 }
@@ -1397,18 +1579,18 @@ u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset)
  * cik_mm_wdoorbell - write a doorbell dword
  *
  * @rdev: radeon_device pointer
- * @offset: byte offset into the aperture
+ * @index: doorbell index
  * @v: value to write
  *
  * Writes @v to the doorbell aperture at the
- * requested offset (CIK).
+ * requested doorbell index (CIK).
  */
-void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v)
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 index, u32 v)
 {
-       if (offset < rdev->doorbell.size) {
-               writel(v, ((void __iomem *)rdev->doorbell.ptr) + offset);
+       if (index < rdev->doorbell.num_doorbells) {
+               writel(v, rdev->doorbell.ptr + index);
        } else {
-               DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", offset);
+               DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", index);
        }
 }
 
@@ -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:
@@ -1981,6 +2427,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
                                gb_tile_moden = 0;
                                break;
                        }
+                       rdev->config.cik.macrotile_mode_array[reg_offset] = gb_tile_moden;
                        WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden);
                }
        } else if (num_pipe_configs == 4) {
@@ -2327,6 +2774,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
                                gb_tile_moden = 0;
                                break;
                        }
+                       rdev->config.cik.macrotile_mode_array[reg_offset] = gb_tile_moden;
                        WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden);
                }
        } else if (num_pipe_configs == 2) {
@@ -2544,6 +2992,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev)
                                gb_tile_moden = 0;
                                break;
                        }
+                       rdev->config.cik.macrotile_mode_array[reg_offset] = gb_tile_moden;
                        WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden);
                }
        } else
@@ -2650,7 +3099,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 +3119,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 +3177,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;
@@ -3084,17 +3559,98 @@ void cik_fence_compute_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, 0);
 }
 
-void cik_semaphore_ring_emit(struct radeon_device *rdev,
+bool cik_semaphore_ring_emit(struct radeon_device *rdev,
                             struct radeon_ring *ring,
                             struct radeon_semaphore *semaphore,
                             bool emit_wait)
 {
+/* TODO: figure out why semaphore cause lockups */
+#if 0
        uint64_t addr = semaphore->gpu_addr;
        unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL;
 
        radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1));
        radeon_ring_write(ring, addr & 0xffffffff);
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel);
+
+       return true;
+#else
+       return false;
+#endif
+}
+
+/**
+ * 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;
+       }
+
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
+
+       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;
 }
 
 /*
@@ -3403,7 +3959,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);
@@ -3500,7 +4057,7 @@ void cik_compute_ring_set_wptr(struct radeon_device *rdev,
                               struct radeon_ring *ring)
 {
        rdev->wb.wb[ring->wptr_offs/4] = cpu_to_le32(ring->wptr);
-       WDOORBELL32(ring->doorbell_offset, ring->wptr);
+       WDOORBELL32(ring->doorbell_index, ring->wptr);
 }
 
 /**
@@ -3841,10 +4398,6 @@ static int cik_cp_compute_resume(struct radeon_device *rdev)
                        return r;
                }
 
-               /* doorbell offset */
-               rdev->ring[idx].doorbell_offset =
-                       (rdev->ring[idx].doorbell_page_num * PAGE_SIZE) + 0;
-
                /* init the mqd struct */
                memset(buf, 0, sizeof(struct bonaire_mqd));
 
@@ -3956,7 +4509,7 @@ static int cik_cp_compute_resume(struct radeon_device *rdev)
                                RREG32(CP_HQD_PQ_DOORBELL_CONTROL);
                        mqd->queue_state.cp_hqd_pq_doorbell_control &= ~DOORBELL_OFFSET_MASK;
                        mqd->queue_state.cp_hqd_pq_doorbell_control |=
-                               DOORBELL_OFFSET(rdev->ring[idx].doorbell_offset / 4);
+                               DOORBELL_OFFSET(rdev->ring[idx].doorbell_index);
                        mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN;
                        mqd->queue_state.cp_hqd_pq_doorbell_control &=
                                ~(DOORBELL_SOURCE | DOORBELL_HIT);
@@ -4740,12 +5293,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 +5392,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 +5560,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 +6059,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 +6281,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 +7626,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;
 
@@ -7332,14 +7840,14 @@ int cik_init(struct radeon_device *rdev)
        ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
        ring->ring_obj = NULL;
        r600_ring_init(rdev, ring, 1024 * 1024);
-       r = radeon_doorbell_get(rdev, &ring->doorbell_page_num);
+       r = radeon_doorbell_get(rdev, &ring->doorbell_index);
        if (r)
                return r;
 
        ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
        ring->ring_obj = NULL;
        r600_ring_init(rdev, ring, 1024 * 1024);
-       r = radeon_doorbell_get(rdev, &ring->doorbell_page_num);
+       r = radeon_doorbell_get(rdev, &ring->doorbell_index);
        if (r)
                return r;
 
@@ -7428,6 +7936,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 b6286068e111613e3b325054dd434bc29f9c4252..0300727a4f70d52c71e24931bbec8335ce6ba6d3 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);
 }
 
 /**
@@ -137,7 +130,7 @@ void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
  * Add a DMA semaphore packet to the ring wait on or signal
  * other rings (CIK).
  */
-void cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
+bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
                                  struct radeon_ring *ring,
                                  struct radeon_semaphore *semaphore,
                                  bool emit_wait)
@@ -148,6 +141,8 @@ void cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SEMAPHORE, 0, extra_bits));
        radeon_ring_write(ring, addr & 0xfffffff8);
        radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
+
+       return true;
 }
 
 /**
@@ -450,13 +445,8 @@ int cik_copy_dma(struct radeon_device *rdev,
                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);
-       }
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
 
        for (i = 0; i < num_loops; i++) {
                cur_size_in_bytes = size_in_bytes;
@@ -653,11 +643,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 +660,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 +674,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 +682,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 +709,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 +747,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 203d2a09a1f55a43768fccce195ac85115efc7f0..5964af5e5b2d8823c4626cad40bfd2abf385fb40 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 91bb470de0a39e4db9713cb58b356a9b6ccbac86..920e1e4a52c52c1edefd84e727a0fc13c2dde39b 100644 (file)
@@ -299,7 +299,9 @@ void cypress_program_response_times(struct radeon_device *rdev)
 static int cypress_pcie_performance_request(struct radeon_device *rdev,
                                            u8 perf_req, bool advertise)
 {
+#if defined(CONFIG_ACPI)
        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+#endif
        u32 tmp;
 
        udelay(10);
index 9fcd338c0fcf6bb1e113c80c8b116bcf5c56abbc..009f46e0ce72db47534d2b7667bcd0119607d91b 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 56f6bec34af538fa899c381ebeb9b79d37c856fc..9702e55e924e8327740869eb34ed455dcd908db3 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 6a0656d00ed0e5a9d643e93542dcd5288e813e01..a37b5443638223e1258b660403730a88a08f448c 100644 (file)
@@ -131,13 +131,8 @@ int evergreen_copy_dma(struct radeon_device *rdev,
                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);
-       }
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
 
        for (i = 0; i < num_loops; i++) {
                cur_size_in_dw = size_in_dw;
index 57fcc4b16a526d166fd6be21955a86d5ee7d87dc..aa695c4feb3d00d393c2e9b74e2395a3a7395164 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 4f6d2962767dced17ae7f5f3e44e79bce4f51bbf..17f990798992dae5d6335726a8f3b4c259659f89 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 cac2866d79da441dfbbbcef860f713c9c7ad5a58..11aab2ab54ce27fb5c0279cbaaad797e95bea42f 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 dd6e9688fbefe6e948226475905d54f1e9499613..bdeb65ed365831db35a94a0ffae523f3c8e419dd 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 f26339028154274609f31247580d03cbbed0d312..cdc003085a76b802eddba1d1ba1a9df5e739dc26 100644 (file)
@@ -3445,9 +3445,9 @@ static int ni_enable_smc_cac(struct radeon_device *rdev,
 static int ni_pcie_performance_request(struct radeon_device *rdev,
                                       u8 perf_req, bool advertise)
 {
+#if defined(CONFIG_ACPI)
        struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
 
-#if defined(CONFIG_ACPI)
        if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) ||
             (perf_req == PCIE_PERF_REQ_PECI_GEN2)) {
                if (eg_pi->pcie_performance_request_registered == false)
index d71333033b2ba0bc63f9e710a23f710d7b96b066..10abc4d5a6cc396a85bb32d4b7d094b748757f99 100644 (file)
@@ -869,13 +869,14 @@ void r100_fence_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, RADEON_SW_INT_FIRE);
 }
 
-void r100_semaphore_ring_emit(struct radeon_device *rdev,
+bool r100_semaphore_ring_emit(struct radeon_device *rdev,
                              struct radeon_ring *ring,
                              struct radeon_semaphore *semaphore,
                              bool emit_wait)
 {
        /* Unused on older asics, since we don't have semaphores or multiple rings */
        BUG();
+       return false;
 }
 
 int r100_copy_blit(struct radeon_device *rdev,
@@ -1434,7 +1435,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 f9be22062df1eb8048384cabec6219e8a5238fff..9ad06732a78bc6d079914a6ce15e92b7e273b265 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)
 {
@@ -2597,7 +2650,7 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
        }
 }
 
-void r600_semaphore_ring_emit(struct radeon_device *rdev,
+bool r600_semaphore_ring_emit(struct radeon_device *rdev,
                              struct radeon_ring *ring,
                              struct radeon_semaphore *semaphore,
                              bool emit_wait)
@@ -2611,6 +2664,8 @@ void r600_semaphore_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1));
        radeon_ring_write(ring, addr & 0xffffffff);
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel);
+
+       return true;
 }
 
 /**
@@ -2653,13 +2708,8 @@ int r600_copy_cpdma(struct radeon_device *rdev,
                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);
-       }
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
 
        radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
        radeon_ring_write(ring, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
index 01a3ec83f284de58b34bd56ff9ec85525efcc0fb..5dceea6f71ae450cf925b0d7e119783c801d6ebd 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 3b317456512a8134ddb681eccd95c82504fd9cce..7844d15c139fcb97247880d596ab4354d4bea8d5 100644 (file)
@@ -311,7 +311,7 @@ void r600_dma_fence_ring_emit(struct radeon_device *rdev,
  * Add a DMA semaphore packet to the ring wait on or signal
  * other rings (r6xx-SI).
  */
-void r600_dma_semaphore_ring_emit(struct radeon_device *rdev,
+bool r600_dma_semaphore_ring_emit(struct radeon_device *rdev,
                                  struct radeon_ring *ring,
                                  struct radeon_semaphore *semaphore,
                                  bool emit_wait)
@@ -322,6 +322,8 @@ void r600_dma_semaphore_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SEMAPHORE, 0, s, 0));
        radeon_ring_write(ring, addr & 0xfffffffc);
        radeon_ring_write(ring, upper_32_bits(addr) & 0xff);
+
+       return true;
 }
 
 /**
@@ -462,13 +464,8 @@ int r600_copy_dma(struct radeon_device *rdev,
                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);
-       }
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
 
        for (i = 0; i < num_loops; i++) {
                cur_size_in_dw = size_in_dw;
index 06022e3b9c3bdc0a0660a60c448e25038659e41d..4b89262f3f0e37242de23b7b6ea2fc555d141da3 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 7b3c7b5932c5a289be3fd58d05b7392791c6a4ff..ebe38724a9765765041eaa245fc7994c4fec01fb 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 24f4960f59ee57be931f3f5301528a859469a016..ecf2a3960c0786ca02fc3aef84afab61e0e6303f 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;
 };
 
@@ -348,6 +348,7 @@ int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence **fence, i
 void radeon_fence_process(struct radeon_device *rdev, int ring);
 bool radeon_fence_signaled(struct radeon_fence *fence);
 int radeon_fence_wait(struct radeon_fence *fence, bool interruptible);
+int radeon_fence_wait_locked(struct radeon_fence *fence);
 int radeon_fence_wait_next_locked(struct radeon_device *rdev, int ring);
 int radeon_fence_wait_empty_locked(struct radeon_device *rdev, int ring);
 int radeon_fence_wait_any(struct radeon_device *rdev,
@@ -548,17 +549,20 @@ struct radeon_semaphore {
        struct radeon_sa_bo             *sa_bo;
        signed                          waiters;
        uint64_t                        gpu_addr;
+       struct radeon_fence             *sync_to[RADEON_NUM_RINGS];
 };
 
 int radeon_semaphore_create(struct radeon_device *rdev,
                            struct radeon_semaphore **semaphore);
-void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring,
+bool radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring,
                                  struct radeon_semaphore *semaphore);
-void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
+bool radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
                                struct radeon_semaphore *semaphore);
+void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore,
+                             struct radeon_fence *fence);
 int radeon_semaphore_sync_rings(struct radeon_device *rdev,
                                struct radeon_semaphore *semaphore,
-                               int signaler, int waiter);
+                               int waiting_ring);
 void radeon_semaphore_free(struct radeon_device *rdev,
                           struct radeon_semaphore **semaphore,
                           struct radeon_fence *fence);
@@ -645,13 +649,15 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg);
 /*
  * GPU doorbell structures, functions & helpers
  */
+#define RADEON_MAX_DOORBELLS 1024      /* Reserve at most 1024 doorbell slots for radeon-owned rings. */
+
 struct radeon_doorbell {
-       u32                     num_pages;
-       bool                    free[1024];
        /* doorbell mmio */
-       resource_size_t                 base;
-       resource_size_t                 size;
-       void __iomem                    *ptr;
+       resource_size_t         base;
+       resource_size_t         size;
+       u32 __iomem             *ptr;
+       u32                     num_doorbells;  /* Number of doorbells actually reserved for radeon. */
+       unsigned long           used[DIV_ROUND_UP(RADEON_MAX_DOORBELLS, BITS_PER_LONG)];
 };
 
 int radeon_doorbell_get(struct radeon_device *rdev, u32 *page);
@@ -765,7 +771,6 @@ struct radeon_ib {
        struct radeon_fence             *fence;
        struct radeon_vm                *vm;
        bool                            is_const_ib;
-       struct radeon_fence             *sync_to[RADEON_NUM_RINGS];
        struct radeon_semaphore         *semaphore;
 };
 
@@ -799,8 +804,7 @@ struct radeon_ring {
        u32 pipe;
        u32 queue;
        struct radeon_bo        *mqd_obj;
-       u32 doorbell_page_num;
-       u32 doorbell_offset;
+       u32 doorbell_index;
        unsigned                wptr_offs;
 };
 
@@ -832,6 +836,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;
@@ -915,7 +925,6 @@ int radeon_ib_get(struct radeon_device *rdev, int ring,
                  struct radeon_ib *ib, struct radeon_vm *vm,
                  unsigned size);
 void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib);
-void radeon_ib_sync_to(struct radeon_ib *ib, struct radeon_fence *fence);
 int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
                       struct radeon_ib *const_ib);
 int radeon_ib_pool_init(struct radeon_device *rdev);
@@ -967,12 +976,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 +1012,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;
@@ -1629,7 +1641,7 @@ struct radeon_asic_ring {
        /* command emmit functions */
        void (*ib_execute)(struct radeon_device *rdev, struct radeon_ib *ib);
        void (*emit_fence)(struct radeon_device *rdev, struct radeon_fence *fence);
-       void (*emit_semaphore)(struct radeon_device *rdev, struct radeon_ring *cp,
+       bool (*emit_semaphore)(struct radeon_device *rdev, struct radeon_ring *cp,
                               struct radeon_semaphore *semaphore, bool emit_wait);
        void (*vm_flush)(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
 
@@ -1675,8 +1687,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,
@@ -1972,6 +1982,7 @@ struct cik_asic {
 
        unsigned tile_config;
        uint32_t tile_mode_array[32];
+       uint32_t macrotile_mode_array[16];
 };
 
 union radeon_asic_config {
@@ -2170,6 +2181,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 +2224,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,
@@ -2228,8 +2243,8 @@ void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v,
 u32 r100_io_rreg(struct radeon_device *rdev, u32 reg);
 void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
 
-u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset);
-void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v);
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 index);
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 index, u32 v);
 
 /*
  * Cast helper
@@ -2292,8 +2307,8 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v);
 #define RREG32_IO(reg) r100_io_rreg(rdev, (reg))
 #define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v))
 
-#define RDOORBELL32(offset) cik_mm_rdoorbell(rdev, (offset))
-#define WDOORBELL32(offset, v) cik_mm_wdoorbell(rdev, (offset), (v))
+#define RDOORBELL32(index) cik_mm_rdoorbell(rdev, (index))
+#define WDOORBELL32(index, v) cik_mm_wdoorbell(rdev, (index), (v))
 
 /*
  * Indirect registers accessor
@@ -2673,8 +2688,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 10f98c7742d8c87289e8bbf677857c781fcebbee..98a9074b306b640644f576e928d4e28701495bb4 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 8f7e04538fd624a5ecb1c302e8b9d5f2455f808a..e354ce94cdd17492030e2bd85ef1406810e9e5df 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,
@@ -2019,6 +2015,8 @@ static struct radeon_asic ci_asic = {
                .bandwidth_update = &dce8_bandwidth_update,
                .get_vblank_counter = &evergreen_get_vblank_counter,
                .wait_for_vblank = &dce4_wait_for_vblank,
+               .set_backlight_level = &atombios_set_backlight_level,
+               .get_backlight_level = &atombios_get_backlight_level,
                .hdmi_enable = &evergreen_hdmi_enable,
                .hdmi_setmode = &evergreen_hdmi_setmode,
        },
@@ -2100,8 +2098,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,
@@ -2119,6 +2116,8 @@ static struct radeon_asic kv_asic = {
                .bandwidth_update = &dce8_bandwidth_update,
                .get_vblank_counter = &evergreen_get_vblank_counter,
                .wait_for_vblank = &dce4_wait_for_vblank,
+               .set_backlight_level = &atombios_set_backlight_level,
+               .get_backlight_level = &atombios_get_backlight_level,
                .hdmi_enable = &evergreen_hdmi_enable,
                .hdmi_setmode = &evergreen_hdmi_setmode,
        },
@@ -2442,27 +2441,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 70c29d5e080dfffdf1a3511e12e0a663c7a57a8f..c9fd97b58076bd366e57e7916f93de5435c58c52 100644 (file)
@@ -80,7 +80,7 @@ int r100_irq_set(struct radeon_device *rdev);
 int r100_irq_process(struct radeon_device *rdev);
 void r100_fence_ring_emit(struct radeon_device *rdev,
                          struct radeon_fence *fence);
-void r100_semaphore_ring_emit(struct radeon_device *rdev,
+bool r100_semaphore_ring_emit(struct radeon_device *rdev,
                              struct radeon_ring *cp,
                              struct radeon_semaphore *semaphore,
                              bool emit_wait);
@@ -313,13 +313,13 @@ int r600_cs_parse(struct radeon_cs_parser *p);
 int r600_dma_cs_parse(struct radeon_cs_parser *p);
 void r600_fence_ring_emit(struct radeon_device *rdev,
                          struct radeon_fence *fence);
-void r600_semaphore_ring_emit(struct radeon_device *rdev,
+bool r600_semaphore_ring_emit(struct radeon_device *rdev,
                              struct radeon_ring *cp,
                              struct radeon_semaphore *semaphore,
                              bool emit_wait);
 void r600_dma_fence_ring_emit(struct radeon_device *rdev,
                              struct radeon_fence *fence);
-void r600_dma_semaphore_ring_emit(struct radeon_device *rdev,
+bool r600_dma_semaphore_ring_emit(struct radeon_device *rdev,
                                  struct radeon_ring *ring,
                                  struct radeon_semaphore *semaphore,
                                  bool emit_wait);
@@ -566,10 +566,6 @@ int sumo_dpm_force_performance_level(struct radeon_device *rdev,
  */
 void cayman_fence_ring_emit(struct radeon_device *rdev,
                            struct radeon_fence *fence);
-void cayman_uvd_semaphore_emit(struct radeon_device *rdev,
-                              struct radeon_ring *ring,
-                              struct radeon_semaphore *semaphore,
-                              bool emit_wait);
 void cayman_pcie_gart_tlb_flush(struct radeon_device *rdev);
 int cayman_init(struct radeon_device *rdev);
 void cayman_fini(struct radeon_device *rdev);
@@ -581,17 +577,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 +650,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);
@@ -696,7 +693,7 @@ void cik_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
 int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
 void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
                              struct radeon_fence *fence);
-void cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
+bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
                                  struct radeon_ring *ring,
                                  struct radeon_semaphore *semaphore,
                                  bool emit_wait);
@@ -705,6 +702,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);
@@ -712,7 +713,7 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev,
                             struct radeon_fence *fence);
 void cik_fence_compute_ring_emit(struct radeon_device *rdev,
                                 struct radeon_fence *fence);
-void cik_semaphore_ring_emit(struct radeon_device *rdev,
+bool cik_semaphore_ring_emit(struct radeon_device *rdev,
                             struct radeon_ring *cp,
                             struct radeon_semaphore *semaphore,
                             bool emit_wait);
@@ -731,11 +732,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,
@@ -802,7 +803,7 @@ void uvd_v1_0_stop(struct radeon_device *rdev);
 
 int uvd_v1_0_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
 int uvd_v1_0_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
-void uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
+bool uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
                             struct radeon_ring *ring,
                             struct radeon_semaphore *semaphore,
                             bool emit_wait);
@@ -814,7 +815,7 @@ void uvd_v2_2_fence_emit(struct radeon_device *rdev,
                         struct radeon_fence *fence);
 
 /* uvd v3.1 */
-void uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
+bool uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
                             struct radeon_ring *ring,
                             struct radeon_semaphore *semaphore,
                             bool emit_wait);
index d96070bf83888c4ddb5ccf00c9b73a1dd0f855ef..9d302eaeea1587b6fdb5439119b6f45c0de961db 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 061b227dae0c45f88f506bf497c75faa1195aca1..b3633d9a531703a1cd4189c061a0907e89ee1085 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 64565732cb98cf25af4e4d1f564e307c85cf4a8f..20a768ac89a8e6532b33cbb3114928afc98749b7 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 80285e35bc6513fda3d5f5c975399a60feaab1e3..f41594b2eeac775794f3d2b2d050a06e387d9025 100644 (file)
@@ -159,7 +159,8 @@ static void radeon_cs_sync_rings(struct radeon_cs_parser *p)
                if (!p->relocs[i].robj)
                        continue;
 
-               radeon_ib_sync_to(&p->ib, p->relocs[i].robj->tbo.sync_obj);
+               radeon_semaphore_sync_to(p->ib.semaphore,
+                                        p->relocs[i].robj->tbo.sync_obj);
        }
 }
 
@@ -212,9 +213,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 +236,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 +283,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 +316,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 +327,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 +335,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 +377,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 +386,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;
@@ -495,9 +412,9 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
                goto out;
        }
        radeon_cs_sync_rings(parser);
-       radeon_ib_sync_to(&parser->ib, vm->fence);
-       radeon_ib_sync_to(&parser->ib, radeon_vm_grab_id(
-               rdev, vm, parser->ring));
+       radeon_semaphore_sync_to(parser->ib.semaphore, vm->fence);
+       radeon_semaphore_sync_to(parser->ib.semaphore,
+                                radeon_vm_grab_id(rdev, vm, parser->ring));
 
        if ((rdev->family >= CHIP_TAHITI) &&
            (parser->chunk_const_ib_idx != -1)) {
@@ -527,6 +444,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 +525,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 +557,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 841d0e09be3e9fb109b5afd45d012590a0ee6433..39b033b441d2a6f76e8d021df7085568538887b3 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.
  *
@@ -244,28 +251,23 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg)
  */
 int radeon_doorbell_init(struct radeon_device *rdev)
 {
-       int i;
-
        /* doorbell bar mapping */
        rdev->doorbell.base = pci_resource_start(rdev->pdev, 2);
        rdev->doorbell.size = pci_resource_len(rdev->pdev, 2);
 
-       /* limit to 4 MB for now */
-       if (rdev->doorbell.size > (4 * 1024 * 1024))
-               rdev->doorbell.size = 4 * 1024 * 1024;
+       rdev->doorbell.num_doorbells = min_t(u32, rdev->doorbell.size / sizeof(u32), RADEON_MAX_DOORBELLS);
+       if (rdev->doorbell.num_doorbells == 0)
+               return -EINVAL;
 
-       rdev->doorbell.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.size);
+       rdev->doorbell.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.num_doorbells * sizeof(u32));
        if (rdev->doorbell.ptr == NULL) {
                return -ENOMEM;
        }
        DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)rdev->doorbell.base);
        DRM_INFO("doorbell mmio size: %u\n", (unsigned)rdev->doorbell.size);
 
-       rdev->doorbell.num_pages = rdev->doorbell.size / PAGE_SIZE;
+       memset(&rdev->doorbell.used, 0, sizeof(rdev->doorbell.used));
 
-       for (i = 0; i < rdev->doorbell.num_pages; i++) {
-               rdev->doorbell.free[i] = true;
-       }
        return 0;
 }
 
@@ -283,40 +285,38 @@ void radeon_doorbell_fini(struct radeon_device *rdev)
 }
 
 /**
- * radeon_doorbell_get - Allocate a doorbell page
+ * radeon_doorbell_get - Allocate a doorbell entry
  *
  * @rdev: radeon_device pointer
- * @doorbell: doorbell page number
+ * @doorbell: doorbell index
  *
- * Allocate a doorbell page for use by the driver (all asics).
+ * Allocate a doorbell for use by the driver (all asics).
  * Returns 0 on success or -EINVAL on failure.
  */
 int radeon_doorbell_get(struct radeon_device *rdev, u32 *doorbell)
 {
-       int i;
-
-       for (i = 0; i < rdev->doorbell.num_pages; i++) {
-               if (rdev->doorbell.free[i]) {
-                       rdev->doorbell.free[i] = false;
-                       *doorbell = i;
-                       return 0;
-               }
+       unsigned long offset = find_first_zero_bit(rdev->doorbell.used, rdev->doorbell.num_doorbells);
+       if (offset < rdev->doorbell.num_doorbells) {
+               __set_bit(offset, rdev->doorbell.used);
+               *doorbell = offset;
+               return 0;
+       } else {
+               return -EINVAL;
        }
-       return -EINVAL;
 }
 
 /**
- * radeon_doorbell_free - Free a doorbell page
+ * radeon_doorbell_free - Free a doorbell entry
  *
  * @rdev: radeon_device pointer
- * @doorbell: doorbell page number
+ * @doorbell: doorbell index
  *
- * Free a doorbell page allocated for use by the driver (all asics)
+ * Free a doorbell allocated for use by the driver (all asics)
  */
 void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell)
 {
-       if (doorbell < rdev->doorbell.num_pages)
-               rdev->doorbell.free[doorbell] = true;
+       if (doorbell < rdev->doorbell.num_doorbells)
+               __clear_bit(doorbell, rdev->doorbell.used);
 }
 
 /*
@@ -1076,7 +1076,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 +1090,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 +1100,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 +1150,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 +1297,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 +1394,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 +1405,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 +1464,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 +1487,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 +1496,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 +1519,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 +1568,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 0d1aa050d41de7b02a1fd12065246c4239d817be..7b253815a3237153b668d660e9ef7d56642597ec 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 9c14a1ba1de43aa9086741fda75c9aba3cc96394..9f5ff28864f6e3ccf4f4736cc55307735d87a70a 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
  *   2.32.0 - new info request for rings working
  *   2.33.0 - Add SI tiling mode array query
  *   2.34.0 - Add CIK tiling mode array query
+ *   2.35.0 - Add CIK macrotile mode array query
  */
 #define KMS_DRIVER_MAJOR       2
-#define KMS_DRIVER_MINOR       34
+#define KMS_DRIVER_MINOR       35
 #define KMS_DRIVER_PATCHLEVEL  0
 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
 int radeon_driver_unload_kms(struct drm_device *dev);
@@ -87,8 +89,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 +102,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 +139,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 +166,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 +228,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 +267,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 +362,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 +508,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 +529,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 +543,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 +585,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 b369d42f7de5681a225529fa29c3d3c5c0fc83b3..543dcfae7e6f5f9a01797a9bb90455be5fef88c5 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 3c8289083f9d4b121bf738f9bc45907012bdfb1a..614ad549297f283ac159de5d73476d7da6107626 100644 (file)
@@ -96,6 +96,7 @@ enum radeon_family {
        CHIP_BONAIRE,
        CHIP_KAVERI,
        CHIP_KABINI,
+       CHIP_HAWAII,
        CHIP_LAST,
 };
 
index ddb8f8e04eb549f4fd264b2f704cdd407bcc65e0..d3a86e43c0123715e0cf765a738e6664f42ab6e0 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)) {
 
-       while (target_seq > atomic64_read(&rdev->fence_drv[ring].last_seq)) {
-               if (!rdev->ring[ring].ready) {
-                       return -EBUSY;
+               /* Save current sequence values, used to check for GPU lockups */
+               for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+                       if (!target_seq[i])
+                               continue;
+
+                       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;
+       seq[fence->ring] = fence->seq;
+       if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ)
+               return 0;
 
-       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_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,21 +452,55 @@ 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;
        }
        return 0;
 }
 
+/**
+ * radeon_fence_wait_locked - wait for a fence to signal
+ *
+ * @fence: radeon fence object
+ *
+ * Wait for the requested fence to signal (all asics).
+ * Returns 0 if the fence has passed, error for all other cases.
+ */
+int radeon_fence_wait_locked(struct radeon_fence *fence)
+{
+       uint64_t seq[RADEON_NUM_RINGS] = {};
+       int r;
+
+       if (fence == NULL) {
+               WARN(1, "Querying an invalid fence : %p !\n", fence);
+               return -EINVAL;
+       }
+
+       seq[fence->ring] = fence->seq;
+       if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ)
+               return 0;
+
+       r = radeon_fence_wait_seq(fence->rdev, seq, false, false);
+       if (r)
+               return r;
+
+       fence->seq = RADEON_FENCE_SIGNALED_SEQ;
+       return 0;
+}
+
 /**
  * radeon_fence_wait_next_locked - wait for the next fence to signal
  *
@@ -594,15 +513,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 +536,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 +749,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 b990b1a2bd50af523e93a7715050a534ba84d987..3044e504f4ec9a8a4cbf6ef18bbd009fe7672c01 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_semaphore_sync_to(ib.semaphore, 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);
 
@@ -913,6 +937,26 @@ uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr)
        return result;
 }
 
+/**
+ * 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
  *
@@ -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,16 @@ 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);
+       if (r)
+               return r;
        ib.length_dw = 0;
 
        r = radeon_vm_update_pdes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset);
@@ -1165,9 +1220,9 @@ 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);
+       radeon_semaphore_sync_to(ib.semaphore, vm->fence);
        r = radeon_ib_schedule(rdev, &ib, NULL);
        if (r) {
                radeon_ib_free(rdev, &ib);
index dce99c8a583501e92b72f4012a250a917835a51f..805c5e566b9a1f29539a4cf1148183056dc90579 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 c180df8e84dbd2b57324c0f6789e48fbb1a07719..bdb0f93e73bcfddcea63d1f8e284af05da742e91 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 cc9e8482cf30d47be57b9fcb8022ebc7d2c9917e..ec6240b00469a18c471e181d59c4670b2e05069d 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 61580ddc4eb2375f908394cc1a119d74d8612aa2..55d0b474bd371ae83f1cea0ec08d30504b371816 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;
@@ -324,7 +340,7 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                break;
        case RADEON_INFO_BACKEND_MAP:
                if (rdev->family >= CHIP_BONAIRE)
-                       return -EINVAL;
+                       *value = rdev->config.cik.backend_map;
                else if (rdev->family >= CHIP_TAHITI)
                        *value = rdev->config.si.backend_map;
                else if (rdev->family >= CHIP_CAYMAN)
@@ -433,6 +449,15 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
                        return -EINVAL;
                }
                break;
+       case RADEON_INFO_CIK_MACROTILE_MODE_ARRAY:
+               if (rdev->family >= CHIP_BONAIRE) {
+                       value = rdev->config.cik.macrotile_mode_array;
+                       value_size = sizeof(uint32_t)*16;
+               } else {
+                       DRM_DEBUG_KMS("macrotile mode array is cik+ only!\n");
+                       return -EINVAL;
+               }
+               break;
        case RADEON_INFO_SI_CP_DMA_COMPUTE:
                *value = 1;
                break;
@@ -475,9 +500,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 +536,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 7cb178a34a0f18c0b50c7cd48a3e151f5ce05b4b..0b158f98d287136df1a4c3db11724798d2973d9f 100644 (file)
@@ -422,6 +422,7 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc,
        /* Pin framebuffer & get tilling informations */
        obj = radeon_fb->obj;
        rbo = gem_to_radeon_bo(obj);
+retry:
        r = radeon_bo_reserve(rbo, false);
        if (unlikely(r != 0))
                return r;
@@ -430,6 +431,33 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc,
                                     &base);
        if (unlikely(r != 0)) {
                radeon_bo_unreserve(rbo);
+
+               /* On old GPU like RN50 with little vram pining can fails because
+                * current fb is taking all space needed. So instead of unpining
+                * the old buffer after pining the new one, first unpin old one
+                * and then retry pining new one.
+                *
+                * As only master can set mode only master can pin and it is
+                * unlikely the master client will race with itself especialy
+                * on those old gpu with single crtc.
+                *
+                * We don't shutdown the display controller because new buffer
+                * will end up in same spot.
+                */
+               if (!atomic && fb && fb != crtc->fb) {
+                       struct radeon_bo *old_rbo;
+                       unsigned long nsize, osize;
+
+                       old_rbo = gem_to_radeon_bo(to_radeon_framebuffer(fb)->obj);
+                       osize = radeon_bo_size(old_rbo);
+                       nsize = radeon_bo_size(rbo);
+                       if (nsize <= osize && !radeon_bo_reserve(old_rbo, false)) {
+                               radeon_bo_unpin(old_rbo);
+                               radeon_bo_unreserve(old_rbo);
+                               fb = NULL;
+                               goto retry;
+                       }
+               }
                return -EINVAL;
        }
        radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
@@ -1056,6 +1084,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 +1113,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 62cd512f5c8d4161ff3708204fe32f560566402e..c89971d904c36696ad17e6ac4ac68f017a81b27c 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 ef63d3f00b2ffb3d212c46315d2f8226a1605f68..3f0dd664af90d6815b2edea895662936cc1982e4 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 4f6b7fc7ad3cad3a90017c6d7f75192c4c55452a..d1385ccc672c4976aff90d9150fb221f98c29441 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,23 @@ int radeon_pm_init(struct radeon_device *rdev)
        case CHIP_RV670:
        case CHIP_RS780:
        case CHIP_RS880:
+       case CHIP_CAYMAN:
+       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 +1283,12 @@ 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 18254e1c3e718ee1c7c4cdd4321bf95b15581970..9214403ae173c146573cf8c4441a6a71cf052665 100644 (file)
@@ -61,7 +61,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring,
                  struct radeon_ib *ib, struct radeon_vm *vm,
                  unsigned size)
 {
-       int i, r;
+       int r;
 
        r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256, true);
        if (r) {
@@ -87,8 +87,6 @@ int radeon_ib_get(struct radeon_device *rdev, int ring,
                ib->gpu_addr = radeon_sa_bo_gpu_addr(ib->sa_bo);
        }
        ib->is_const_ib = false;
-       for (i = 0; i < RADEON_NUM_RINGS; ++i)
-               ib->sync_to[i] = NULL;
 
        return 0;
 }
@@ -108,25 +106,6 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib)
        radeon_fence_unref(&ib->fence);
 }
 
-/**
- * radeon_ib_sync_to - sync to fence before executing the IB
- *
- * @ib: IB object to add fence to
- * @fence: fence to sync to
- *
- * Sync to the fence before executing the IB
- */
-void radeon_ib_sync_to(struct radeon_ib *ib, struct radeon_fence *fence)
-{
-       struct radeon_fence *other;
-
-       if (!fence)
-               return;
-
-       other = ib->sync_to[fence->ring];
-       ib->sync_to[fence->ring] = radeon_fence_later(fence, other);
-}
-
 /**
  * radeon_ib_schedule - schedule an IB (Indirect Buffer) on the ring
  *
@@ -151,8 +130,7 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
                       struct radeon_ib *const_ib)
 {
        struct radeon_ring *ring = &rdev->ring[ib->ring];
-       bool need_sync = false;
-       int i, r = 0;
+       int r = 0;
 
        if (!ib->length_dw || !ring->ready) {
                /* TODO: Nothings in the ib we should report. */
@@ -166,19 +144,15 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
                dev_err(rdev->dev, "scheduling IB failed (%d).\n", r);
                return r;
        }
-       for (i = 0; i < RADEON_NUM_RINGS; ++i) {
-               struct radeon_fence *fence = ib->sync_to[i];
-               if (radeon_fence_need_sync(fence, ib->ring)) {
-                       need_sync = true;
-                       radeon_semaphore_sync_rings(rdev, ib->semaphore,
-                                                   fence->ring, ib->ring);
-                       radeon_fence_note_sync(fence, ib->ring);
-               }
-       }
-       /* immediately free semaphore when we don't need to sync */
-       if (!need_sync) {
-               radeon_semaphore_free(rdev, &ib->semaphore, NULL);
+
+       /* sync with other rings */
+       r = radeon_semaphore_sync_rings(rdev, ib->semaphore, ib->ring);
+       if (r) {
+               dev_err(rdev->dev, "failed to sync rings (%d)\n", r);
+               radeon_ring_unlock_undo(rdev, ring);
+               return r;
        }
+
        /* if we can't remember our last VM flush then flush now! */
        /* XXX figure out why we have to flush for every IB */
        if (ib->vm /*&& !ib->vm->last_flush*/) {
index 8dcc20f53d734ebe12b24f57b175e69988d93309..2b42aa1914f2006dabddee990cd14fc919668d59 100644 (file)
  */
 #include <drm/drmP.h>
 #include "radeon.h"
-
+#include "radeon_trace.h"
 
 int radeon_semaphore_create(struct radeon_device *rdev,
                            struct radeon_semaphore **semaphore)
 {
-       int r;
+       int i, r;
 
        *semaphore = kmalloc(sizeof(struct radeon_semaphore), GFP_KERNEL);
        if (*semaphore == NULL) {
@@ -50,54 +50,121 @@ int radeon_semaphore_create(struct radeon_device *rdev,
        (*semaphore)->waiters = 0;
        (*semaphore)->gpu_addr = radeon_sa_bo_gpu_addr((*semaphore)->sa_bo);
        *((uint64_t*)radeon_sa_bo_cpu_addr((*semaphore)->sa_bo)) = 0;
+
+       for (i = 0; i < RADEON_NUM_RINGS; ++i)
+               (*semaphore)->sync_to[i] = NULL;
+
        return 0;
 }
 
-void radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring,
+bool radeon_semaphore_emit_signal(struct radeon_device *rdev, int ridx,
                                  struct radeon_semaphore *semaphore)
 {
-       --semaphore->waiters;
-       radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, false);
+       struct radeon_ring *ring = &rdev->ring[ridx];
+
+       trace_radeon_semaphore_signale(ridx, semaphore);
+
+       if (radeon_semaphore_ring_emit(rdev, ridx, ring, semaphore, false)) {
+               --semaphore->waiters;
+
+               /* for debugging lockup only, used by sysfs debug files */
+               ring->last_semaphore_signal_addr = semaphore->gpu_addr;
+               return true;
+       }
+       return false;
 }
 
-void radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
+bool radeon_semaphore_emit_wait(struct radeon_device *rdev, int ridx,
                                struct radeon_semaphore *semaphore)
 {
-       ++semaphore->waiters;
-       radeon_semaphore_ring_emit(rdev, ring, &rdev->ring[ring], semaphore, true);
+       struct radeon_ring *ring = &rdev->ring[ridx];
+
+       trace_radeon_semaphore_wait(ridx, semaphore);
+
+       if (radeon_semaphore_ring_emit(rdev, ridx, ring, semaphore, true)) {
+               ++semaphore->waiters;
+
+               /* for debugging lockup only, used by sysfs debug files */
+               ring->last_semaphore_wait_addr = semaphore->gpu_addr;
+               return true;
+       }
+       return false;
+}
+
+/**
+ * radeon_semaphore_sync_to - use the semaphore to sync to a fence
+ *
+ * @semaphore: semaphore object to add fence to
+ * @fence: fence to sync to
+ *
+ * Sync to the fence using this semaphore object
+ */
+void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore,
+                             struct radeon_fence *fence)
+{
+        struct radeon_fence *other;
+
+        if (!fence)
+                return;
+
+        other = semaphore->sync_to[fence->ring];
+        semaphore->sync_to[fence->ring] = radeon_fence_later(fence, other);
 }
 
-/* caller must hold ring lock */
+/**
+ * radeon_semaphore_sync_rings - sync ring to all registered fences
+ *
+ * @rdev: radeon_device pointer
+ * @semaphore: semaphore object to use for sync
+ * @ring: ring that needs sync
+ *
+ * Ensure that all registered fences are signaled before letting
+ * the ring continue. The caller must hold the ring lock.
+ */
 int radeon_semaphore_sync_rings(struct radeon_device *rdev,
                                struct radeon_semaphore *semaphore,
-                               int signaler, int waiter)
+                               int ring)
 {
-       int r;
+       int i, r;
 
-       /* no need to signal and wait on the same ring */
-       if (signaler == waiter) {
-               return 0;
-       }
+        for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+               struct radeon_fence *fence = semaphore->sync_to[i];
 
-       /* prevent GPU deadlocks */
-       if (!rdev->ring[signaler].ready) {
-               dev_err(rdev->dev, "Trying to sync to a disabled ring!");
-               return -EINVAL;
-       }
+               /* check if we really need to sync */
+                if (!radeon_fence_need_sync(fence, ring))
+                       continue;
 
-       r = radeon_ring_alloc(rdev, &rdev->ring[signaler], 8);
-       if (r) {
-               return r;
-       }
-       radeon_semaphore_emit_signal(rdev, signaler, semaphore);
-       radeon_ring_commit(rdev, &rdev->ring[signaler]);
+               /* prevent GPU deadlocks */
+               if (!rdev->ring[i].ready) {
+                       dev_err(rdev->dev, "Syncing to a disabled ring!");
+                       return -EINVAL;
+               }
 
-       /* we assume caller has already allocated space on waiters ring */
-       radeon_semaphore_emit_wait(rdev, waiter, semaphore);
+               /* allocate enough space for sync command */
+               r = radeon_ring_alloc(rdev, &rdev->ring[i], 16);
+               if (r) {
+                       return r;
+               }
 
-       /* for debugging lockup only, used by sysfs debug files */
-       rdev->ring[signaler].last_semaphore_signal_addr = semaphore->gpu_addr;
-       rdev->ring[waiter].last_semaphore_wait_addr = semaphore->gpu_addr;
+               /* emit the signal semaphore */
+               if (!radeon_semaphore_emit_signal(rdev, i, semaphore)) {
+                       /* signaling wasn't successful wait manually */
+                       radeon_ring_undo(&rdev->ring[i]);
+                       radeon_fence_wait_locked(fence);
+                       continue;
+               }
+
+               /* we assume caller has already allocated space on waiters ring */
+               if (!radeon_semaphore_emit_wait(rdev, ring, semaphore)) {
+                       /* waiting wasn't successful wait manually */
+                       radeon_ring_undo(&rdev->ring[i]);
+                       radeon_fence_wait_locked(fence);
+                       continue;
+               }
+
+               radeon_ring_commit(rdev, &rdev->ring[i]);
+               radeon_fence_note_sync(fence, ring);
+       }
 
        return 0;
 }
index f7e367815964f34756a13691935018ae95978ca1..9f0e18172b6e8074bf865e93902d67f1584141bd 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),
@@ -87,6 +111,42 @@ DEFINE_EVENT(radeon_fence_request, radeon_fence_wait_end,
            TP_ARGS(dev, seqno)
 );
 
+DECLARE_EVENT_CLASS(radeon_semaphore_request,
+
+           TP_PROTO(int ring, struct radeon_semaphore *sem),
+
+           TP_ARGS(ring, sem),
+
+           TP_STRUCT__entry(
+                            __field(int, ring)
+                            __field(signed, waiters)
+                            __field(uint64_t, gpu_addr)
+                            ),
+
+           TP_fast_assign(
+                          __entry->ring = ring;
+                          __entry->waiters = sem->waiters;
+                          __entry->gpu_addr = sem->gpu_addr;
+                          ),
+
+           TP_printk("ring=%u, waiters=%d, addr=%010Lx", __entry->ring,
+                     __entry->waiters, __entry->gpu_addr)
+);
+
+DEFINE_EVENT(radeon_semaphore_request, radeon_semaphore_signale,
+
+           TP_PROTO(int ring, struct radeon_semaphore *sem),
+
+           TP_ARGS(ring, sem)
+);
+
+DEFINE_EVENT(radeon_semaphore_request, radeon_semaphore_wait,
+
+           TP_PROTO(int ring, struct radeon_semaphore *sem),
+
+           TP_ARGS(ring, sem)
+);
+
 #endif
 
 /* This part must be outside protection */
index 33858364fe89badf7fd45e3f98382d0c6d5df732..a77cd274dfc3fbe003d66dcbed21f3bdcb12fbcf 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 308eff5be1b420b74b18ccb1b1e0c89f4bd67f32..373d088bac66db910291045424e075921d39d202 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 6acba8017b9afd24d63b906a2fb9b8ec5934ec1f..76cc8d3aafec461d0b41668f25500f2a4753dbc3 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 1447d794c22ad21648408a390e4b78bc84493af0..1c560629575a8906fb74e006285096f07eac7f19 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 873eb4b193b4f86c35fa2cc02417adbdee54344d..5d1c316115efa31ab7b029de44db66e399322e71 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 5811d277a36a60ec845cbcadf9588326a01a488b..26633a0252522051bc5a504357d93bd9b6257c67 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 f9b02e3d683006f40897971b0a2d457e8b61f0bb..aca8cbe8a335dce1d13487c60e13f3193630fec7 100644 (file)
@@ -66,13 +66,8 @@ int rv770_copy_dma(struct radeon_device *rdev,
                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);
-       }
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
 
        for (i = 0; i < num_loops; i++) {
                cur_size_in_dw = size_in_dw;
index d96f7cbca0a115f58eaeca9df3a6e585d6b3445d..6a64ccaa0695643add9ee7e6b3f383202d35a988 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 49909d23dfce4073d666df31a3625c3c8668b56d..59be2cfcbb472c1a538f058978645a0b775f5f49 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);
@@ -199,13 +195,8 @@ int si_copy_dma(struct radeon_device *rdev,
                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);
-       }
+       radeon_semaphore_sync_to(sem, *fence);
+       radeon_semaphore_sync_rings(rdev, sem, ring->idx);
 
        for (i = 0; i < num_loops; i++) {
                cur_size_in_bytes = size_in_bytes;
index 2332aa1bf93c7c40936710c7e8595c6d49f6514b..0b00c790fb7713d8b4cbddb91c9b319ecc41c3b9 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 7e2e0ea66a008f491f5396bada706ca469b23f0b..b322acc48097f632a0fa1ac3204facc3287912f5 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 9364129ba292e1b5412a0d10818ed158c95bb0fa..d700698a1f224bea67bd585061289e56735072e4 100644 (file)
@@ -1873,9 +1873,9 @@ int trinity_dpm_init(struct radeon_device *rdev)
        pi->enable_sclk_ds = true;
        pi->enable_gfx_power_gating = true;
        pi->enable_gfx_clock_gating = true;
-       pi->enable_mg_clock_gating = true;
-       pi->enable_gfx_dynamic_mgpg = true; /* ??? */
-       pi->override_dynamic_mgpg = true;
+       pi->enable_mg_clock_gating = false;
+       pi->enable_gfx_dynamic_mgpg = false;
+       pi->override_dynamic_mgpg = false;
        pi->enable_auto_thermal_throttling = true;
        pi->voltage_drop_in_dce = false; /* need to restructure dpm/modeset interaction */
        pi->uvd_dpm = true; /* ??? */
index 7266805d9786c6fe9bfd38501f7ab674f7d05a73..d4a68af1a2792125dc58bbc955938c42174f723b 100644 (file)
@@ -357,7 +357,7 @@ int uvd_v1_0_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
  *
  * Emit a semaphore command (either wait or signal) to the UVD ring.
  */
-void uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
+bool uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
                             struct radeon_ring *ring,
                             struct radeon_semaphore *semaphore,
                             bool emit_wait)
@@ -372,6 +372,8 @@ void uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
 
        radeon_ring_write(ring, PACKET0(UVD_SEMA_CMD, 0));
        radeon_ring_write(ring, emit_wait ? 1 : 0);
+
+       return true;
 }
 
 /**
index 5b6fa1f62d4ecbe5c84abb0473550331c3a968dc..d722db2cf340e6a4017f648b8cd0f9876cb3e0c6 100644 (file)
@@ -37,7 +37,7 @@
  *
  * Emit a semaphore command (either wait or signal) to the UVD ring.
  */
-void uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
+bool uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
                             struct radeon_ring *ring,
                             struct radeon_semaphore *semaphore,
                             bool emit_wait)
@@ -52,4 +52,6 @@ void uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
 
        radeon_ring_write(ring, PACKET0(UVD_SEMA_CMD, 0));
        radeon_ring_write(ring, 0x80 | (emit_wait ? 1 : 0));
+
+       return true;
 }
index c590cd9dca0bc5d707bf1c8a2fdce62fb3946d59..d8e835ac2c5eabf65d5cdcd26d27147aaf7dfc8d 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 ca498d151a76597de53a8f96bfe19f2279ac29bb..2ee44ca9d67f9e3e6873831fec3b0fdeefd77e29 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 54bad98e9477589742eb21fecb10fd30f560f209..562f9a401cf65996bb5b0fa99982451271537736 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 69853a4de40aecd85774d234640778646a552b2c..8961ba6a34b879246e9b90defc1b129c2ebeea0f 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 b1a05ad901c3d4758f4d768efc5b276d2dc4ee8e..ae1cb31ead7e4256c32f6ea045395347780a25a8 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 79eaec9aac7752a27bddd06a4c08cf7f2292eac8..91bbda291470c29ce3ba80a70b0e5d541b8c6556 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 02ce020f25754633bca0f9489e6e29c85ec9eea2..fdfe259ed7f8e5e403f984d533b54a3257128aef 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 979a3e32b78bbb0f5da3126b6b406eebae6ef497..490f7719e317ed80319f4961a69d3e349d1ad333 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 59623de4ee15f3bf9a186d80481f358397fbf99e..28a9cbc07ab95f3a5873fc9aac0f008180bffca5 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 492533a2dacb1bba249cde2ff08b63e1c855c291..7674000bf47d6696ecec6db7507926144832c772 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 644d95c7d489997b9305f6eced5e62b638798a54..0cd9bc2056e8c6bfe12cd15c5722c6f18d087e78 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 52ac36e08ccbef5ec14df106abeabcdd12efe6f2..0aebc485f7fa36be983a609f693bfbb06dcb0214 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 137ae81ab80eb164d139742b16996bca8b8922b5..2cb0065e0578f6d80da0532dad68a31e27a6924d 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 5aa66ef7a946f8b1c25a4241cf39f76dad9e91b7..ba47ca4fb880cc239a9b5c454b3b6c5b5adcddc8 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 7a4d10106906e92e91e75763e0d96b07557bfbff..7c3ef79fcb3736a80636c12fe49de1f8c8b3a735 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 b2b33dde2afb06a1c78ffcbeb2e826a28ec0bb58..b433b9f040c984ff6e6703f33fa45601d8fac81d 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 f1a857ec1021e81754a79c384a035cd948910b2b..07e02c4bf5a8e0def00e120a14b5ada2d1cfaa97 100644 (file)
@@ -151,7 +151,7 @@ static void ttm_bo_release_list(struct kref *list_kref)
        atomic_dec(&bo->glob->bo_count);
        if (bo->resv == &bo->ttm_resv)
                reservation_object_fini(&bo->ttm_resv);
-
+       mutex_destroy(&bo->wu_mutex);
        if (bo->destroy)
                bo->destroy(bo);
        else {
@@ -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);
        }
        /*
@@ -1103,6 +1123,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
        INIT_LIST_HEAD(&bo->ddestroy);
        INIT_LIST_HEAD(&bo->swap);
        INIT_LIST_HEAD(&bo->io_reserve_lru);
+       mutex_init(&bo->wu_mutex);
        bo->bdev = bdev;
        bo->glob = bdev->glob;
        bo->type = type;
@@ -1684,3 +1705,35 @@ void ttm_bo_swapout_all(struct ttm_bo_device *bdev)
                ;
 }
 EXPORT_SYMBOL(ttm_bo_swapout_all);
+
+/**
+ * ttm_bo_wait_unreserved - interruptible wait for a buffer object to become
+ * unreserved
+ *
+ * @bo: Pointer to buffer
+ */
+int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo)
+{
+       int ret;
+
+       /*
+        * In the absense of a wait_unlocked API,
+        * Use the bo::wu_mutex to avoid triggering livelocks due to
+        * concurrent use of this function. Note that this use of
+        * bo::wu_mutex can go away if we change locking order to
+        * mmap_sem -> bo::reserve.
+        */
+       ret = mutex_lock_interruptible(&bo->wu_mutex);
+       if (unlikely(ret != 0))
+               return -ERESTARTSYS;
+       if (!ww_mutex_is_locked(&bo->resv->lock))
+               goto out_unlock;
+       ret = ttm_bo_reserve_nolru(bo, true, false, false, NULL);
+       if (unlikely(ret != 0))
+               goto out_unlock;
+       ww_mutex_unlock(&bo->resv->lock);
+
+out_unlock:
+       mutex_unlock(&bo->wu_mutex);
+       return ret;
+}
index 7cc904d3a4d12b4efd6935f75ed302335552a4aa..15b86a94949dcb875d3cd716322875ad8c763e88 100644 (file)
@@ -343,19 +343,28 @@ 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;
-       if (old_iomap == NULL && ttm == NULL)
+
+       /*
+        * Don't move nonexistent data. Clear destination instead.
+        */
+       if (old_iomap == NULL &&
+           (ttm == NULL || ttm->state == tt_unpopulated)) {
+               memset_io(new_iomap, 0, new_mem->num_pages*PAGE_SIZE);
                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 +390,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 +409,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 1006c15445e9753764305c00f652a86891f81d64..b249ab9b1eb29c402d407049a42c2fda83b3ffb1 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,17 +102,33 @@ 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
         * between mmap_sem and bo_reserve: Perform a trylock operation
-        * for reserve, and if it fails, retry the fault after scheduling.
+        * for reserve, and if it fails, retry the fault after waiting
+        * for the buffer to become unreserved.
         */
-
-       ret = ttm_bo_reserve(bo, true, true, false, 0);
+       ret = ttm_bo_reserve(bo, true, true, false, NULL);
        if (unlikely(ret != 0)) {
-               if (ret == -EBUSY)
-                       set_need_resched();
+               if (ret != -EBUSY)
+                       return VM_FAULT_NOPAGE;
+
+               if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) {
+                       if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) {
+                               up_read(&vma->vm_mm->mmap_sem);
+                               (void) ttm_bo_wait_unreserved(bo);
+                       }
+
+                       return VM_FAULT_RETRY;
+               }
+
+               /*
+                * If we'd want to change locking order to
+                * mmap_sem -> bo::reserve, we'd use a blocking reserve here
+                * instead of retrying the fault...
+                */
                return VM_FAULT_NOPAGE;
        }
 
@@ -77,7 +138,6 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                case 0:
                        break;
                case -EBUSY:
-                       set_need_resched();
                case -ERESTARTSYS:
                        retval = VM_FAULT_NOPAGE;
                        goto out_unlock;
@@ -91,18 +151,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 +179,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 +220,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 6c911789ae5cc979d46e649f9e5f39287df63d71..479e9418e3d710a8c5f02fab2355f2e6991e933c 100644 (file)
@@ -32,8 +32,7 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 
-static void ttm_eu_backoff_reservation_locked(struct list_head *list,
-                                             struct ww_acquire_ctx *ticket)
+static void ttm_eu_backoff_reservation_locked(struct list_head *list)
 {
        struct ttm_validate_buffer *entry;
 
@@ -93,8 +92,9 @@ void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
        entry = list_first_entry(list, struct ttm_validate_buffer, head);
        glob = entry->bo->glob;
        spin_lock(&glob->lru_lock);
-       ttm_eu_backoff_reservation_locked(list, ticket);
-       ww_acquire_fini(ticket);
+       ttm_eu_backoff_reservation_locked(list);
+       if (ticket)
+               ww_acquire_fini(ticket);
        spin_unlock(&glob->lru_lock);
 }
 EXPORT_SYMBOL(ttm_eu_backoff_reservation);
@@ -130,7 +130,8 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
        entry = list_first_entry(list, struct ttm_validate_buffer, head);
        glob = entry->bo->glob;
 
-       ww_acquire_init(ticket, &reservation_ww_class);
+       if (ticket)
+               ww_acquire_init(ticket, &reservation_ww_class);
 retry:
        list_for_each_entry(entry, list, head) {
                struct ttm_buffer_object *bo = entry->bo;
@@ -139,16 +140,17 @@ retry:
                if (entry->reserved)
                        continue;
 
-
-               ret = ttm_bo_reserve_nolru(bo, true, false, true, ticket);
+               ret = ttm_bo_reserve_nolru(bo, true, (ticket == NULL), true,
+                                          ticket);
 
                if (ret == -EDEADLK) {
                        /* uh oh, we lost out, drop every reservation and try
                         * to only reserve this buffer, then start over if
                         * this succeeds.
                         */
+                       BUG_ON(ticket == NULL);
                        spin_lock(&glob->lru_lock);
-                       ttm_eu_backoff_reservation_locked(list, ticket);
+                       ttm_eu_backoff_reservation_locked(list);
                        spin_unlock(&glob->lru_lock);
                        ttm_eu_list_ref_sub(list);
                        ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
@@ -175,7 +177,8 @@ retry:
                }
        }
 
-       ww_acquire_done(ticket);
+       if (ticket)
+               ww_acquire_done(ticket);
        spin_lock(&glob->lru_lock);
        ttm_eu_del_from_lru_locked(list);
        spin_unlock(&glob->lru_lock);
@@ -184,12 +187,14 @@ retry:
 
 err:
        spin_lock(&glob->lru_lock);
-       ttm_eu_backoff_reservation_locked(list, ticket);
+       ttm_eu_backoff_reservation_locked(list);
        spin_unlock(&glob->lru_lock);
        ttm_eu_list_ref_sub(list);
 err_fini:
-       ww_acquire_done(ticket);
-       ww_acquire_fini(ticket);
+       if (ticket) {
+               ww_acquire_done(ticket);
+               ww_acquire_fini(ticket);
+       }
        return ret;
 }
 EXPORT_SYMBOL(ttm_eu_reserve_buffers);
@@ -224,7 +229,8 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
        }
        spin_unlock(&bdev->fence_lock);
        spin_unlock(&glob->lru_lock);
-       ww_acquire_fini(ticket);
+       if (ticket)
+               ww_acquire_fini(ticket);
 
        list_for_each_entry(entry, list, head) {
                if (entry->old_sync_obj)
index a868176c258a95aee788253038637b83ec54a78f..6fe7b92a82d1f72f465a79d48dd0088337d713fe 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
  *
- * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright (c) 2009-2013 VMware, Inc., Palo Alto, CA., USA
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  **************************************************************************/
 /*
  * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ *
+ * While no substantial code is shared, the prime code is inspired by
+ * drm_prime.c, with
+ * Authors:
+ *      Dave Airlie <airlied@redhat.com>
+ *      Rob Clark <rob.clark@linaro.org>
  */
 /** @file ttm_ref_object.c
  *
@@ -34,6 +40,7 @@
  * and release on file close.
  */
 
+
 /**
  * struct ttm_object_file
  *
@@ -84,6 +91,9 @@ struct ttm_object_device {
        struct drm_open_hash object_hash;
        atomic_t object_count;
        struct ttm_mem_global *mem_glob;
+       struct dma_buf_ops ops;
+       void (*dmabuf_release)(struct dma_buf *dma_buf);
+       size_t dma_buf_size;
 };
 
 /**
@@ -116,6 +126,8 @@ struct ttm_ref_object {
        struct ttm_object_file *tfile;
 };
 
+static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf);
+
 static inline struct ttm_object_file *
 ttm_object_file_ref(struct ttm_object_file *tfile)
 {
@@ -416,9 +428,10 @@ out_err:
 }
 EXPORT_SYMBOL(ttm_object_file_init);
 
-struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global
-                                                *mem_glob,
-                                                unsigned int hash_order)
+struct ttm_object_device *
+ttm_object_device_init(struct ttm_mem_global *mem_glob,
+                      unsigned int hash_order,
+                      const struct dma_buf_ops *ops)
 {
        struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL);
        int ret;
@@ -430,10 +443,17 @@ struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global
        spin_lock_init(&tdev->object_lock);
        atomic_set(&tdev->object_count, 0);
        ret = drm_ht_create(&tdev->object_hash, hash_order);
+       if (ret != 0)
+               goto out_no_object_hash;
 
-       if (likely(ret == 0))
-               return tdev;
+       tdev->ops = *ops;
+       tdev->dmabuf_release = tdev->ops.release;
+       tdev->ops.release = ttm_prime_dmabuf_release;
+       tdev->dma_buf_size = ttm_round_pot(sizeof(struct dma_buf)) +
+               ttm_round_pot(sizeof(struct file));
+       return tdev;
 
+out_no_object_hash:
        kfree(tdev);
        return NULL;
 }
@@ -452,3 +472,225 @@ void ttm_object_device_release(struct ttm_object_device **p_tdev)
        kfree(tdev);
 }
 EXPORT_SYMBOL(ttm_object_device_release);
+
+/**
+ * get_dma_buf_unless_doomed - get a dma_buf reference if possible.
+ *
+ * @dma_buf: Non-refcounted pointer to a struct dma-buf.
+ *
+ * Obtain a file reference from a lookup structure that doesn't refcount
+ * the file, but synchronizes with its release method to make sure it has
+ * not been freed yet. See for example kref_get_unless_zero documentation.
+ * Returns true if refcounting succeeds, false otherwise.
+ *
+ * Nobody really wants this as a public API yet, so let it mature here
+ * for some time...
+ */
+static bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf)
+{
+       return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L;
+}
+
+/**
+ * ttm_prime_refcount_release - refcount release method for a prime object.
+ *
+ * @p_base: Pointer to ttm_base_object pointer.
+ *
+ * This is a wrapper that calls the refcount_release founction of the
+ * underlying object. At the same time it cleans up the prime object.
+ * This function is called when all references to the base object we
+ * derive from are gone.
+ */
+static void ttm_prime_refcount_release(struct ttm_base_object **p_base)
+{
+       struct ttm_base_object *base = *p_base;
+       struct ttm_prime_object *prime;
+
+       *p_base = NULL;
+       prime = container_of(base, struct ttm_prime_object, base);
+       BUG_ON(prime->dma_buf != NULL);
+       mutex_destroy(&prime->mutex);
+       if (prime->refcount_release)
+               prime->refcount_release(&base);
+}
+
+/**
+ * ttm_prime_dmabuf_release - Release method for the dma-bufs we export
+ *
+ * @dma_buf:
+ *
+ * This function first calls the dma_buf release method the driver
+ * provides. Then it cleans up our dma_buf pointer used for lookup,
+ * and finally releases the reference the dma_buf has on our base
+ * object.
+ */
+static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf)
+{
+       struct ttm_prime_object *prime =
+               (struct ttm_prime_object *) dma_buf->priv;
+       struct ttm_base_object *base = &prime->base;
+       struct ttm_object_device *tdev = base->tfile->tdev;
+
+       if (tdev->dmabuf_release)
+               tdev->dmabuf_release(dma_buf);
+       mutex_lock(&prime->mutex);
+       if (prime->dma_buf == dma_buf)
+               prime->dma_buf = NULL;
+       mutex_unlock(&prime->mutex);
+       ttm_mem_global_free(tdev->mem_glob, tdev->dma_buf_size);
+       ttm_base_object_unref(&base);
+}
+
+/**
+ * ttm_prime_fd_to_handle - Get a base object handle from a prime fd
+ *
+ * @tfile: A struct ttm_object_file identifying the caller.
+ * @fd: The prime / dmabuf fd.
+ * @handle: The returned handle.
+ *
+ * This function returns a handle to an object that previously exported
+ * a dma-buf. Note that we don't handle imports yet, because we simply
+ * have no consumers of that implementation.
+ */
+int ttm_prime_fd_to_handle(struct ttm_object_file *tfile,
+                          int fd, u32 *handle)
+{
+       struct ttm_object_device *tdev = tfile->tdev;
+       struct dma_buf *dma_buf;
+       struct ttm_prime_object *prime;
+       struct ttm_base_object *base;
+       int ret;
+
+       dma_buf = dma_buf_get(fd);
+       if (IS_ERR(dma_buf))
+               return PTR_ERR(dma_buf);
+
+       if (dma_buf->ops != &tdev->ops)
+               return -ENOSYS;
+
+       prime = (struct ttm_prime_object *) dma_buf->priv;
+       base = &prime->base;
+       *handle = base->hash.key;
+       ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+
+       dma_buf_put(dma_buf);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ttm_prime_fd_to_handle);
+
+/**
+ * ttm_prime_handle_to_fd - Return a dma_buf fd from a ttm prime object
+ *
+ * @tfile: Struct ttm_object_file identifying the caller.
+ * @handle: Handle to the object we're exporting from.
+ * @flags: flags for dma-buf creation. We just pass them on.
+ * @prime_fd: The returned file descriptor.
+ *
+ */
+int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
+                          uint32_t handle, uint32_t flags,
+                          int *prime_fd)
+{
+       struct ttm_object_device *tdev = tfile->tdev;
+       struct ttm_base_object *base;
+       struct dma_buf *dma_buf;
+       struct ttm_prime_object *prime;
+       int ret;
+
+       base = ttm_base_object_lookup(tfile, handle);
+       if (unlikely(base == NULL ||
+                    base->object_type != ttm_prime_type)) {
+               ret = -ENOENT;
+               goto out_unref;
+       }
+
+       prime = container_of(base, struct ttm_prime_object, base);
+       if (unlikely(!base->shareable)) {
+               ret = -EPERM;
+               goto out_unref;
+       }
+
+       ret = mutex_lock_interruptible(&prime->mutex);
+       if (unlikely(ret != 0)) {
+               ret = -ERESTARTSYS;
+               goto out_unref;
+       }
+
+       dma_buf = prime->dma_buf;
+       if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) {
+
+               /*
+                * Need to create a new dma_buf, with memory accounting.
+                */
+               ret = ttm_mem_global_alloc(tdev->mem_glob, tdev->dma_buf_size,
+                                          false, true);
+               if (unlikely(ret != 0)) {
+                       mutex_unlock(&prime->mutex);
+                       goto out_unref;
+               }
+
+               dma_buf = dma_buf_export(prime, &tdev->ops,
+                                        prime->size, flags);
+               if (IS_ERR(dma_buf)) {
+                       ret = PTR_ERR(dma_buf);
+                       ttm_mem_global_free(tdev->mem_glob,
+                                           tdev->dma_buf_size);
+                       mutex_unlock(&prime->mutex);
+                       goto out_unref;
+               }
+
+               /*
+                * dma_buf has taken the base object reference
+                */
+               base = NULL;
+               prime->dma_buf = dma_buf;
+       }
+       mutex_unlock(&prime->mutex);
+
+       ret = dma_buf_fd(dma_buf, flags);
+       if (ret >= 0) {
+               *prime_fd = ret;
+               ret = 0;
+       } else
+               dma_buf_put(dma_buf);
+
+out_unref:
+       if (base)
+               ttm_base_object_unref(&base);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ttm_prime_handle_to_fd);
+
+/**
+ * ttm_prime_object_init - Initialize a ttm_prime_object
+ *
+ * @tfile: struct ttm_object_file identifying the caller
+ * @size: The size of the dma_bufs we export.
+ * @prime: The object to be initialized.
+ * @shareable: See ttm_base_object_init
+ * @type: See ttm_base_object_init
+ * @refcount_release: See ttm_base_object_init
+ * @ref_obj_release: See ttm_base_object_init
+ *
+ * Initializes an object which is compatible with the drm_prime model
+ * for data sharing between processes and devices.
+ */
+int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size,
+                         struct ttm_prime_object *prime, bool shareable,
+                         enum ttm_object_type type,
+                         void (*refcount_release) (struct ttm_base_object **),
+                         void (*ref_obj_release) (struct ttm_base_object *,
+                                                  enum ttm_ref_type ref_type))
+{
+       mutex_init(&prime->mutex);
+       prime->size = PAGE_ALIGN(size);
+       prime->real_type = type;
+       prime->dma_buf = NULL;
+       prime->refcount_release = refcount_release;
+       return ttm_base_object_init(tfile, &prime->base, shareable,
+                                   ttm_prime_type,
+                                   ttm_prime_refcount_release,
+                                   ref_obj_release);
+}
+EXPORT_SYMBOL(ttm_prime_object_init);
index 7957beeeaf733752e58c6b57f5d5a5a531113a09..fb8259f698395a286e28fbe1053bc70e04821694 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 6222af19f4566591f81f9e319b006c59b13a1f5b..f02528686cd524745e9ad058f12b7b8cc56a7620 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 7650dc0d78cec2391755a04235cfe3bfc6c7a8d1..3ddd6cd98ac12d0d8cbf9124556f6688b677c9f8 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 56aec9409fa317c37054705e0a54c724cb126068..1fbf7b357f16ba1c9b6a8baea1fd54292398e041 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 8bf646183bac837a24f335bb46237c5c4a5d5e43..24ffbe990736e3e0751609e78d113d08c952b69f 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 7e3ad87c366c6761e41b38c4fe3226f38925d9e5..9278891054834992ba30c5b9ac2a4f5b09403438 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 2cc6cd91ac11da8829a349bbf7b523ec4f0905dc..9f8b690bcf52c97e24db7c6a515f9be185b30321 100644 (file)
@@ -6,6 +6,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
            vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
            vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \
            vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \
-           vmwgfx_surface.o
+           vmwgfx_surface.o vmwgfx_prime.o
 
 obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
index 96dc84dc34d05122c877322b3415eff84d64af39..7776e6f0aef650d475e39640b3d781b42f8d7036 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 0508f93b9795fbc2b4ccf9c8f34ea62383a7e138..c7a549694e59fb2614562627dcb7f167680babcd 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;
@@ -587,7 +677,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        dev_priv->tdev = ttm_object_device_init
-           (dev_priv->mem_global_ref.object, 12);
+               (dev_priv->mem_global_ref.object, 12, &vmw_prime_dmabuf_ops);
 
        if (unlikely(dev_priv->tdev == NULL)) {
                DRM_ERROR("Unable to initialize TTM object management.\n");
@@ -1120,7 +1210,7 @@ static const struct file_operations vmwgfx_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
-       DRIVER_MODESET,
+       DRIVER_MODESET | DRIVER_PRIME,
        .load = vmw_driver_load,
        .unload = vmw_driver_unload,
        .lastclose = vmw_lastclose,
@@ -1145,6 +1235,9 @@ static struct drm_driver driver = {
        .dumb_map_offset = vmw_dumb_map_offset,
        .dumb_destroy = vmw_dumb_destroy,
 
+       .prime_fd_to_handle = vmw_prime_fd_to_handle,
+       .prime_handle_to_fd = vmw_prime_handle_to_fd,
+
        .fops = &vmwgfx_driver_fops,
        .name = VMWGFX_DRIVER_NAME,
        .desc = VMWGFX_DRIVER_DESC,
index 150ec64af617e4974250a6deb36a5c6234766341..db85985c7086f04648b6a4bb89b83f352c393ec2 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
@@ -722,6 +818,20 @@ int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv);
 
 extern const struct ttm_mem_type_manager_func vmw_gmrid_manager_func;
 
+/**
+ * Prime - vmwgfx_prime.c
+ */
+
+extern const struct dma_buf_ops vmw_prime_dmabuf_ops;
+extern int vmw_prime_fd_to_handle(struct drm_device *dev,
+                                 struct drm_file *file_priv,
+                                 int fd, u32 *handle);
+extern int vmw_prime_handle_to_fd(struct drm_device *dev,
+                                 struct drm_file *file_priv,
+                                 uint32_t handle, uint32_t flags,
+                                 int *prime_fd);
+
+
 /**
  * Inline helper functions
  */
index 1a0bf07fe54b8c41dd16ab274c48ca6cc6043d32..6ef0b035becbc5959f78ecf7b978629f90ae98e2 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 c509d40c4897ad9e1bdf8fd37d6618c9ccb44be0..a51f48e3e917e0d3f5d4c73202be335df6f6919b 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 fc43c060123679b1ec0ab7a6ac674bae80990ba1..ecb3d867b4260d9c9ada27438fb252dac1fc77e9 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;
        }
 
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c
new file mode 100644 (file)
index 0000000..31fe32d
--- /dev/null
@@ -0,0 +1,137 @@
+/**************************************************************************
+ *
+ * Copyright Â© 2013 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+/*
+ * Authors:
+ *     Thomas Hellstrom <thellstrom@vmware.com>
+ *
+ */
+
+#include "vmwgfx_drv.h"
+#include <linux/dma-buf.h>
+#include <drm/ttm/ttm_object.h>
+
+/*
+ * DMA-BUF attach- and mapping methods. No need to implement
+ * these until we have other virtual devices use them.
+ */
+
+static int vmw_prime_map_attach(struct dma_buf *dma_buf,
+                               struct device *target_dev,
+                               struct dma_buf_attachment *attach)
+{
+       return -ENOSYS;
+}
+
+static void vmw_prime_map_detach(struct dma_buf *dma_buf,
+                                struct dma_buf_attachment *attach)
+{
+}
+
+static struct sg_table *vmw_prime_map_dma_buf(struct dma_buf_attachment *attach,
+                                             enum dma_data_direction dir)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static void vmw_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
+                                   struct sg_table *sgb,
+                                   enum dma_data_direction dir)
+{
+}
+
+static void *vmw_prime_dmabuf_vmap(struct dma_buf *dma_buf)
+{
+       return NULL;
+}
+
+static void vmw_prime_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
+{
+}
+
+static void *vmw_prime_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
+               unsigned long page_num)
+{
+       return NULL;
+}
+
+static void vmw_prime_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
+               unsigned long page_num, void *addr)
+{
+
+}
+static void *vmw_prime_dmabuf_kmap(struct dma_buf *dma_buf,
+               unsigned long page_num)
+{
+       return NULL;
+}
+
+static void vmw_prime_dmabuf_kunmap(struct dma_buf *dma_buf,
+               unsigned long page_num, void *addr)
+{
+
+}
+
+static int vmw_prime_dmabuf_mmap(struct dma_buf *dma_buf,
+                                struct vm_area_struct *vma)
+{
+       WARN_ONCE(true, "Attempted use of dmabuf mmap. Bad.\n");
+       return -ENOSYS;
+}
+
+const struct dma_buf_ops vmw_prime_dmabuf_ops =  {
+       .attach = vmw_prime_map_attach,
+       .detach = vmw_prime_map_detach,
+       .map_dma_buf = vmw_prime_map_dma_buf,
+       .unmap_dma_buf = vmw_prime_unmap_dma_buf,
+       .release = NULL,
+       .kmap = vmw_prime_dmabuf_kmap,
+       .kmap_atomic = vmw_prime_dmabuf_kmap_atomic,
+       .kunmap = vmw_prime_dmabuf_kunmap,
+       .kunmap_atomic = vmw_prime_dmabuf_kunmap_atomic,
+       .mmap = vmw_prime_dmabuf_mmap,
+       .vmap = vmw_prime_dmabuf_vmap,
+       .vunmap = vmw_prime_dmabuf_vunmap,
+};
+
+int vmw_prime_fd_to_handle(struct drm_device *dev,
+                          struct drm_file *file_priv,
+                          int fd, u32 *handle)
+{
+       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+
+       return ttm_prime_fd_to_handle(tfile, fd, handle);
+}
+
+int vmw_prime_handle_to_fd(struct drm_device *dev,
+                          struct drm_file *file_priv,
+                          uint32_t handle, uint32_t flags,
+                          int *prime_fd)
+{
+       struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+
+       return ttm_prime_handle_to_fd(tfile, handle, flags, prime_fd);
+}
index 37fb4befec82634ccf3a504428af8ed60f4a0e56..efe2b74c5eb17f1f86b12048be5884c9aabf50b2 100644 (file)
 #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 ttm_prime_object prime;
        struct vmw_dma_buffer dma;
 };
 
@@ -295,7 +297,7 @@ int vmw_user_resource_lookup_handle(struct vmw_private *dev_priv,
        if (unlikely(base == NULL))
                return -EINVAL;
 
-       if (unlikely(base->object_type != converter->object_type))
+       if (unlikely(ttm_base_object_type(base) != converter->object_type))
                goto out_bad_resource;
 
        res = converter->base_obj_to_res(base);
@@ -385,7 +387,7 @@ static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)
 {
        struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo);
 
-       ttm_base_object_kfree(vmw_user_bo, base);
+       ttm_prime_object_kfree(vmw_user_bo, prime);
 }
 
 static void vmw_user_dmabuf_release(struct ttm_base_object **p_base)
@@ -399,7 +401,8 @@ static void vmw_user_dmabuf_release(struct ttm_base_object **p_base)
        if (unlikely(base == NULL))
                return;
 
-       vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
+       vmw_user_bo = container_of(base, struct vmw_user_dma_buffer,
+                                  prime.base);
        bo = &vmw_user_bo->dma.base;
        ttm_bo_unref(&bo);
 }
@@ -440,18 +443,19 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv,
                return ret;
 
        tmp = ttm_bo_reference(&user_bo->dma.base);
-       ret = ttm_base_object_init(tfile,
-                                  &user_bo->base,
-                                  shareable,
-                                  ttm_buffer_type,
-                                  &vmw_user_dmabuf_release, NULL);
+       ret = ttm_prime_object_init(tfile,
+                                   size,
+                                   &user_bo->prime,
+                                   shareable,
+                                   ttm_buffer_type,
+                                   &vmw_user_dmabuf_release, NULL);
        if (unlikely(ret != 0)) {
                ttm_bo_unref(&tmp);
                goto out_no_base_object;
        }
 
        *p_dma_buf = &user_bo->dma;
-       *handle = user_bo->base.hash.key;
+       *handle = user_bo->prime.base.hash.key;
 
 out_no_base_object:
        return ret;
@@ -473,8 +477,8 @@ int vmw_user_dmabuf_verify_access(struct ttm_buffer_object *bo,
                return -EPERM;
 
        vmw_user_bo = vmw_user_dma_buffer(bo);
-       return (vmw_user_bo->base.tfile == tfile ||
-       vmw_user_bo->base.shareable) ? 0 : -EPERM;
+       return (vmw_user_bo->prime.base.tfile == tfile ||
+               vmw_user_bo->prime.base.shareable) ? 0 : -EPERM;
 }
 
 int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
@@ -536,14 +540,15 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
                return -ESRCH;
        }
 
-       if (unlikely(base->object_type != ttm_buffer_type)) {
+       if (unlikely(ttm_base_object_type(base) != ttm_buffer_type)) {
                ttm_base_object_unref(&base);
                printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
                       (unsigned long)handle);
                return -EINVAL;
        }
 
-       vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
+       vmw_user_bo = container_of(base, struct vmw_user_dma_buffer,
+                                  prime.base);
        (void)ttm_bo_reference(&vmw_user_bo->dma.base);
        ttm_base_object_unref(&base);
        *out = &vmw_user_bo->dma;
@@ -560,7 +565,8 @@ int vmw_user_dmabuf_reference(struct ttm_object_file *tfile,
                return -EINVAL;
 
        user_bo = container_of(dma_buf, struct vmw_user_dma_buffer, dma);
-       return ttm_ref_object_add(tfile, &user_bo->base, TTM_REF_USAGE, NULL);
+       return ttm_ref_object_add(tfile, &user_bo->prime.base,
+                                 TTM_REF_USAGE, NULL);
 }
 
 /*
@@ -805,15 +811,16 @@ int vmw_dumb_create(struct drm_file *file_priv,
                goto out_no_dmabuf;
 
        tmp = ttm_bo_reference(&vmw_user_bo->dma.base);
-       ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile,
-                                  &vmw_user_bo->base,
-                                  false,
-                                  ttm_buffer_type,
-                                  &vmw_user_dmabuf_release, NULL);
+       ret = ttm_prime_object_init(vmw_fpriv(file_priv)->tfile,
+                                   args->size,
+                                   &vmw_user_bo->prime,
+                                   false,
+                                   ttm_buffer_type,
+                                   &vmw_user_dmabuf_release, NULL);
        if (unlikely(ret != 0))
                goto out_no_base_object;
 
-       args->handle = vmw_user_bo->base.hash.key;
+       args->handle = vmw_user_bo->prime.base.hash.key;
 
 out_no_base_object:
        ttm_bo_unref(&tmp);
@@ -992,7 +999,6 @@ void vmw_resource_unreserve(struct vmw_resource *res,
  */
 static int
 vmw_resource_check_buffer(struct vmw_resource *res,
-                         struct ww_acquire_ctx *ticket,
                          bool interruptible,
                          struct ttm_validate_buffer *val_buf)
 {
@@ -1009,7 +1015,7 @@ vmw_resource_check_buffer(struct vmw_resource *res,
        INIT_LIST_HEAD(&val_list);
        val_buf->bo = ttm_bo_reference(&res->backup->base);
        list_add_tail(&val_buf->head, &val_list);
-       ret = ttm_eu_reserve_buffers(ticket, &val_list);
+       ret = ttm_eu_reserve_buffers(NULL, &val_list);
        if (unlikely(ret != 0))
                goto out_no_reserve;
 
@@ -1027,7 +1033,7 @@ vmw_resource_check_buffer(struct vmw_resource *res,
        return 0;
 
 out_no_validate:
-       ttm_eu_backoff_reservation(ticket, &val_list);
+       ttm_eu_backoff_reservation(NULL, &val_list);
 out_no_reserve:
        ttm_bo_unref(&val_buf->bo);
        if (backup_dirty)
@@ -1072,8 +1078,7 @@ int vmw_resource_reserve(struct vmw_resource *res, bool no_backup)
  * @val_buf:        Backup buffer information.
  */
 static void
-vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
-                                struct ttm_validate_buffer *val_buf)
+vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf)
 {
        struct list_head val_list;
 
@@ -1082,7 +1087,7 @@ vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
 
        INIT_LIST_HEAD(&val_list);
        list_add_tail(&val_buf->head, &val_list);
-       ttm_eu_backoff_reservation(ticket, &val_list);
+       ttm_eu_backoff_reservation(NULL, &val_list);
        ttm_bo_unref(&val_buf->bo);
 }
 
@@ -1091,18 +1096,18 @@ 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;
-       struct ww_acquire_ctx ticket;
        int ret;
 
        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, interruptible, &val_buf);
        if (unlikely(ret != 0))
                return ret;
 
@@ -1117,7 +1122,7 @@ int vmw_resource_do_evict(struct vmw_resource *res)
        res->backup_dirty = true;
        res->res_dirty = false;
 out_no_unbind:
-       vmw_resource_backoff_reservation(&ticket, &val_buf);
+       vmw_resource_backoff_reservation(&val_buf);
 
        return ret;
 }
@@ -1141,6 +1146,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 +1161,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 +1174,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 +1271,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 +1292,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 582814339748c2fa194921ab8034d7e629088367..7de2ea8bd55357561913384bfa23196fe3479ec2 100644 (file)
@@ -38,7 +38,7 @@
  * @size:           TTM accounting size for the surface.
  */
 struct vmw_user_surface {
-       struct ttm_base_object base;
+       struct ttm_prime_object prime;
        struct vmw_surface srf;
        uint32_t size;
        uint32_t backup_handle;
@@ -580,7 +580,8 @@ static int vmw_surface_init(struct vmw_private *dev_priv,
 static struct vmw_resource *
 vmw_user_surface_base_to_res(struct ttm_base_object *base)
 {
-       return &(container_of(base, struct vmw_user_surface, base)->srf.res);
+       return &(container_of(base, struct vmw_user_surface,
+                             prime.base)->srf.res);
 }
 
 /**
@@ -599,7 +600,7 @@ static void vmw_user_surface_free(struct vmw_resource *res)
        kfree(srf->offsets);
        kfree(srf->sizes);
        kfree(srf->snooper.image);
-       ttm_base_object_kfree(user_srf, base);
+       ttm_prime_object_kfree(user_srf, prime);
        ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
 }
 
@@ -616,7 +617,7 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base)
 {
        struct ttm_base_object *base = *p_base;
        struct vmw_user_surface *user_srf =
-           container_of(base, struct vmw_user_surface, base);
+           container_of(base, struct vmw_user_surface, prime.base);
        struct vmw_resource *res = &user_srf->srf.res;
 
        *p_base = NULL;
@@ -790,8 +791,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
        }
        srf->snooper.crtc = NULL;
 
-       user_srf->base.shareable = false;
-       user_srf->base.tfile = NULL;
+       user_srf->prime.base.shareable = false;
+       user_srf->prime.base.tfile = NULL;
 
        /**
         * From this point, the generic resource management functions
@@ -803,9 +804,9 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
                goto out_unlock;
 
        tmp = vmw_resource_reference(&srf->res);
-       ret = ttm_base_object_init(tfile, &user_srf->base,
-                                  req->shareable, VMW_RES_SURFACE,
-                                  &vmw_user_surface_base_release, NULL);
+       ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime,
+                                   req->shareable, VMW_RES_SURFACE,
+                                   &vmw_user_surface_base_release, NULL);
 
        if (unlikely(ret != 0)) {
                vmw_resource_unreference(&tmp);
@@ -813,7 +814,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
                goto out_unlock;
        }
 
-       rep->sid = user_srf->base.hash.key;
+       rep->sid = user_srf->prime.base.hash.key;
        vmw_resource_unreference(&res);
 
        ttm_read_unlock(&vmaster->lock);
@@ -823,7 +824,7 @@ out_no_copy:
 out_no_offsets:
        kfree(srf->sizes);
 out_no_sizes:
-       ttm_base_object_kfree(user_srf, base);
+       ttm_prime_object_kfree(user_srf, prime);
 out_no_user_srf:
        ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
 out_unlock:
@@ -859,13 +860,14 @@ int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       if (unlikely(base->object_type != VMW_RES_SURFACE))
+       if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE))
                goto out_bad_resource;
 
-       user_srf = container_of(base, struct vmw_user_surface, base);
+       user_srf = container_of(base, struct vmw_user_surface, prime.base);
        srf = &user_srf->srf;
 
-       ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL);
+       ret = ttm_ref_object_add(tfile, &user_srf->prime.base,
+                                TTM_REF_USAGE, NULL);
        if (unlikely(ret != 0)) {
                DRM_ERROR("Could not add a reference to a surface.\n");
                goto out_no_reference;
index ccfd42b236060ffa17329d95a114029ef1faa89c..7d6bed2225422fa2413130a606d2b20fd084cfd4 100644 (file)
@@ -19,6 +19,4 @@ config TEGRA_HOST1X_FIREWALL
 
          If unsure, choose Y.
 
-source "drivers/gpu/host1x/drm/Kconfig"
-
 endif
index 3b037b6e0298454edbc44c30db4f818f3ca8cafc..afa1e9e4e51265fbdb21698072315786a6b730b7 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 9b85f10f4a44fc9f70a45af7b40f33d95ba9942c..4099e99212c87354377d9a851d5409573792611b 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 de72172d3b5fc2ac87955f3ef8b711890454434c..3995255b16c753731420cb8283992c17c33ddf2e 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 48723b8eea42da33e9854a0925bbeedfa391a37a..df767cf90d51e14c2647f42ef192a7ffcf19489b 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 471630299878a88bbf300a09a0600da2c1c062eb..80da003d63de8d4b754cd37a607441c7c4901574 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 bed90a8131be590fef951c533cbf3622708184da..a61a976e7a421a405d7fe075272228f08e3e3e6d 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 2ee4ad55c4dba344cbb28d72d8febecd7f0181ee..37e2a63241a9d6150b1554896fde15ac0294c157 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 ee199623e36570719ad35fbe3de0d41b00744843..4608257ab65641c488f627a0789eae9a4ba90ff1 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 334c038052f582cac72c1818d30b3387aa3ae060..640c75ca5a8bbc465dcc9f1350d4f5efdd928d5d 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 a14e91cd1e58eee8e5f52db20c672076544a0439..859b73beb4d0c326fcaf24ae1086ff2bc876df09 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 42f3ce19ca32bbb03147c74e11d3c1172b1e8b57..f7553599ee275c0a7c2eddb32ba4a6821fb25de9 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 b592eef1efcb9babfe0355d418f4d276682a0841..b26dcc83bc1b373400d72c46aad21d04fb97f52f 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 0cf6095d33678f51da127f4feac313f9422bc9e8..56e85395ac24112faf17b4b06a1aa950e671df0b 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 c4e1050f2252679e448e58e1c2b3edffffd08073..de5ec333ce1adc1974001d01bad56d2544d472d3 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 fba45f20458e2d1a4b4e83a795c2d948eaa5ea08..33a697d6dcefea290c3bac2981e2399e27f99a14 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;
@@ -55,105 +46,6 @@ struct host1x_job_unpin_data {
        struct sg_table *sgt;
 };
 
-/*
- * 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.
  */
index 409745b949dbfa7d9ecd897409147ae0225623e3..159c479829c959d0bd898742f9bcfec54e74020e 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 267c0b9d3647d24f2a3bf7dc81504eacaf957005..9056465ecd3f1ea88963c397b6f2fdd0ae5f407a 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 c91d547191dd2b7a511b1564523c8f649ec6ac2d..34e2d39d4ce814cb312de6eee14dae43179cb5c3 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,9 +456,11 @@ 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
+         - Wistron optical touch panels
          - XAT optical touch panels
          - Xiroku optical touch panels
          - Zytronic touch panels
@@ -614,6 +619,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 a959f4aecaf5754d1fd9499b1bad386e57615df5..30e44318f87f6c72742f6b6f21584a7ac3af200e 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 881cf7b4f9a433f8ae3e0f6721b3789aa66731f7..497558127bb3e63609af2b5e1f75265f9e7b1423 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 a42e6a394c5ec33d663f7a00eadd2b362c24763d..0e6a42d37eb6f374ef864f383fc23d15bf844736 100644 (file)
@@ -297,6 +297,9 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id)
 
        appleir->hid = hid;
 
+       /* force input as some remotes bypass the input registration */
+       hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
+
        spin_lock_init(&appleir->lock);
        setup_timer(&appleir->key_up_timer,
                    key_up_tick, (unsigned long) appleir);
index 64ab94a55aa796515958206986a991003a750069..a594e478a1e218abd629b69f8ca9b19b9335e753 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 e80da62363bc2c96d6be419f89d002f3a55e4d36..253fe23ef7fe5332ea03f96449f312a69a6b80c0 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) },
@@ -1723,6 +1723,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) },
@@ -1754,6 +1755,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 +1803,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) },
@@ -1871,7 +1880,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
-       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
 };
@@ -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 f042a6cf8b18c897fce2586f108bd91daaf9d33e..4e49462870abdf2f265c1528ae6c07fa4f783ec8 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 e696566cde46420334d1f416b53c2675ec581a7b..0caa676de622fb03e2126ffe1f512e47b3c86567 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");
@@ -65,6 +67,8 @@ static const struct hid_device_id holtek_mouse_devices[] = {
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) },
        { 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 f0296a50be5f754fc7be2b8f593bf2b2beca1e22..f9304cb37154982da4de6774894c213bf412163f 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_VENDOR_ID_KYE              0x0458
 #define USB_DEVICE_ID_KYE_ERGO_525V    0x0087
 #define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138
+#define USB_DEVICE_ID_GENIUS_MANTICORE 0x0153
 #define USB_DEVICE_ID_GENIUS_GX_IMPERATOR      0x4018
 #define USB_DEVICE_ID_KYE_GPEN_560     0x5003
 #define USB_DEVICE_ID_KYE_EASYPEN_I405X        0x5010
 #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_NEXTWINDOW_TOUCHSCREEN   0x0003
 
 #define USB_VENDOR_ID_NINTENDO         0x057e
-#define USB_VENDOR_ID_NINTENDO2                0x054c
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE2        0x0330
 
 #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
 
 #define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802
 #define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804
 
+#define USB_VENDOR_ID_WISTRON          0x0fb8
+#define USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH            0x1109
+
 #define USB_VENDOR_ID_X_TENSIONS               0x1ae7
 #define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE    0x9001
 
index 73845120295eba6f678679715c9ca3f97917a643..ecb5ca669e97615cb47cbb9d31381bf76e33f367 100644 (file)
@@ -341,6 +341,9 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        case USB_DEVICE_ID_GENIUS_GX_IMPERATOR:
                rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 83,
                                        "Genius Gx Imperator Keyboard");
+       case USB_DEVICE_ID_GENIUS_MANTICORE:
+               rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104,
+                                       "Genius Manticore Keyboard");
                break;
        }
        return rdesc;
@@ -418,6 +421,14 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
                        goto enabling_err;
                }
                break;
+       case USB_DEVICE_ID_GENIUS_MANTICORE:
+               /*
+                * The manticore keyboard needs to have all the interfaces
+                * opened at least once to be fully functional.
+                */
+               if (hid_hw_open(hdev))
+                       hid_hw_close(hdev);
+               break;
        }
 
        return 0;
@@ -439,6 +450,8 @@ static const struct hid_device_id kye_devices[] = {
                                USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE,
                                USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KYE,
+                               USB_DEVICE_ID_GENIUS_MANTICORE) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, kye_devices);
index 31cf29a6ba17551ff5244a0f243d48a4719e662c..2d25b6cbbc051910a8eeaaab0525605c77a88da1 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 6f12ecd36c8834fa9c9cb77b79d883dd4c246dc8..06eb45fa6331fee64152754a5ab9bb2275e3a61c 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 1a42eaa6ca0234a054631544732db69484c5eb82..0e3fb1a7e42174dd1dfb953e7209fc14ab50ed7a 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 8782fe1aaa0796e42d17f1ad39d1c72c652d58c5..befe0e336471b0f36e34473ee97c3ba72759ddf9 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 2e5302462efb088b3f3019b26fedfc816ca493f5..a7947d8251a88af3ecb017a7e597556cd0c046ff 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 5e5fe1b8eebb73e5d4533997a889eee87d637150..d83b1e8b505b512769d8c5955eb8a0b8ca4e5b23 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,
@@ -1312,6 +1335,12 @@ static const struct hid_device_id mt_devices[] = {
        { .driver_data = MT_CLS_NSMU,
                MT_USB_DEVICE(USB_VENDOR_ID_UNITEC,
                        USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
+
+       /* Wistron panels */
+       { .driver_data = MT_CLS_NSMU,
+               MT_USB_DEVICE(USB_VENDOR_ID_WISTRON,
+                       USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH) },
+
        /* XAT */
        { .driver_data = MT_CLS_NSMU,
                MT_USB_DEVICE(USB_VENDOR_ID_XAT,
index 74f704032627eb0a30920f69de78c1d3b9df4263..02e28e9f4ea7dcb64165df74ee20be1faccc92ac 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 a97746a63b7020a7cdb999ee8bf9ae5f3af47a66..eaa56eb7d5d18e454c01aea3b2536dbc0ff4f3a9 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 99a605ebb665b6eb952f99922bc7103a0536819f..07de2f9014c67f914209a5dfc08185892955bd79 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 0c8e1ef0b67d14fdb1c3bf2a6b575fdea94faf8e..966047711fbfc91038912382d0303291c937070e 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 0332267199d5bd058becaf8c2d295628e0ba29ac..6dbf6e04dce75c82dd32109d46b330d5a60cd043 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 9120ba72087f7d7e024dce882a1013e9646cb01b..d23217bd2b868aa437d726cfe13754d0c167cfee 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 88fc5aefcd964b2f8b6c83476ca7d308066069f1..a184e1921c11190ffce1033e5b7d4f512d55f798 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 b18320db5f7d18cba708ecd306ee3f394abebddb..098af2f84b8c653dca9542d78de33c5c5777fe8b 100644 (file)
@@ -225,6 +225,13 @@ static const unsigned int buzz_keymap[] = {
 struct sony_sc {
        unsigned long quirks;
 
+#ifdef CONFIG_SONY_FF
+       struct work_struct rumble_worker;
+       struct hid_device *hdev;
+       __u8 left;
+       __u8 right;
+#endif
+
        void *extra;
 };
 
@@ -419,21 +426,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 +621,76 @@ static void buzz_remove(struct hid_device *hdev)
        drv_data->extra = NULL;
 }
 
+#ifdef CONFIG_SONY_FF
+static void sony_rumble_worker(struct work_struct *work)
+{
+       struct sony_sc *sc = container_of(work, struct sony_sc, rumble_worker);
+       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
+       };
+
+       buf[3] = sc->right;
+       buf[5] = sc->left;
+
+       sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf),
+                                       HID_OUTPUT_REPORT);
+}
+
+static int sony_play_effect(struct input_dev *dev, void *data,
+                           struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct sony_sc *sc = hid_get_drvdata(hid);
+
+       if (effect->type != FF_RUMBLE)
+               return 0;
+
+       sc->left = effect->u.rumble.strong_magnitude / 256;
+       sc->right = effect->u.rumble.weak_magnitude ? 1 : 0;
+
+       schedule_work(&sc->rumble_worker);
+       return 0;
+}
+
+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;
+       struct sony_sc *sc = hid_get_drvdata(hdev);
+
+       sc->hdev = hdev;
+       INIT_WORK(&sc->rumble_worker, sony_rumble_worker);
+
+       input_set_capability(input_dev, EV_FF, FF_RUMBLE);
+       return input_ff_create_memless(input_dev, NULL, sony_play_effect);
+}
+
+static void sony_destroy_ff(struct hid_device *hdev)
+{
+       struct sony_sc *sc = hid_get_drvdata(hdev);
+
+       cancel_work_sync(&sc->rumble_worker);
+}
+
+#else
+static int sony_init_ff(struct hid_device *hdev)
+{
+       return 0;
+}
+
+static void sony_destroy_ff(struct hid_device *hdev)
+{
+}
+#endif
+
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret;
@@ -670,6 +740,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);
@@ -683,6 +757,8 @@ static void sony_remove(struct hid_device *hdev)
        if (sc->quirks & BUZZ_CONTROLLER)
                buzz_remove(hdev);
 
+       sony_destroy_ff(hdev);
+
        hid_hw_stop(hdev);
 }
 
index 1446f526ee8bbade2290b615fc14b6aae35dc09f..abb20db2b443ccdcc34159a97fcc83307db65c40 100644 (file)
@@ -834,8 +834,7 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
                goto done;
        }
 
-       if (vendor == USB_VENDOR_ID_NINTENDO ||
-           vendor == USB_VENDOR_ID_NINTENDO2) {
+       if (vendor == USB_VENDOR_ID_NINTENDO) {
                if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) {
                        devtype = WIIMOTE_DEV_GEN10;
                        goto done;
@@ -1856,8 +1855,6 @@ static void wiimote_hid_remove(struct hid_device *hdev)
 static const struct hid_device_id wiimote_hid_devices[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
                                USB_DEVICE_ID_NINTENDO_WIIMOTE) },
-       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO2,
-                               USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
                                USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
index 71adf9e60b13f4aafa22ba94183bb4dcb8a3a21d..6b61f01e01e7fad839f8dd960050596a26a9abfe 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 75db0c4000377f03bf262eb66a5492046aa012c8..10934aa129fbb7743d90dadbd540e9b164b14c38 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 fd7ce374f812ead7960b72a5d00ac3b5908ab221..5f7e55f4b7f052e29f754f78cabe8dd6a4593fb4 100644 (file)
@@ -454,10 +454,6 @@ static void i2c_hid_init_reports(struct hid_device *hid)
                return;
        }
 
-       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 93b00d76374cee2b82be0a9deab2a0f8d7c6755b..cedc6da93c19c5c77136127feb207a424899a321 100644 (file)
@@ -287,7 +287,7 @@ static int uhid_event_from_user(const char __user *buffer, size_t len,
                         */
                        struct uhid_create_req_compat *compat;
 
-                       compat = kmalloc(sizeof(*compat), GFP_KERNEL);
+                       compat = kzalloc(sizeof(*compat), GFP_KERNEL);
                        if (!compat)
                                return -ENOMEM;
 
index 3fca3be08337d76fdd8ecaec09c3d1693b0ac4ef..0db9a67278ba21d3d574b3f209311d5f54fecbac 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 b3ab9d43bb3e8591c7d43c8728e5a3493f7b710e..52d548f1dc1ddbccceedab0c7f2bf9e1d53766f2 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 8d40da314a8e5bf5b424a5039e4279ba819433d1..6a34f7f48eb9172272a25d4467bf1a190f7dc1e3 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 1d7ff46812c3dc9d72abb977b02cbf4db04fbd2c..dafc63c6932dfa47001a0269fc283984c16c321e 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/err.h>
 
 #include <acpi/acpi.h>
-#include <acpi/acpixf.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 
index e0d66b9590abd21d91440abe3fc7015dc21f4276..a183e488db78ba2531f349c688ff0283764f2578 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 c03b490bba813287d1d6368338743b450f34e687..7e3ef134f1d2ec0f0132e705cce2f8b586bf487d 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 cdff74282955a9005fa2d8ea648866f21b59055c..4c4c1421bf28f66462d8d0bf5f2ee70947005ac0 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 d17325db0ea3d9f2478fc2fd7c42641603797e92..cf811c1a14756f3180e4b91243f1c10c85a800e1 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 cdcbd8368ed344d205d7edb2edcdfe90919fd42f..3b26129f605587eeead9193d7d3eb06157bce622 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 d00997f3eb3bc650e172d7addb5a83552db8936e..c73eb0ea788e6dde96604b1e8e4401bc087eef8f 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 fd059308affa2985e64142756c9176f356f0d744..8edba9de76df17ff4d0c973daafd89d4a1510f40 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..18a74a6
--- /dev/null
@@ -0,0 +1,908 @@
+/*
+ * 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/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, bcm_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 ea4b08fc3353a99f75ac64eaf11bd3c1370d2913..77df97b932af5399a39b0c52ec7e256aff4c8325 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);
 
@@ -299,6 +299,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
        strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
        adap->algo = &bcm2835_i2c_algo;
        adap->dev.parent = &pdev->dev;
+       adap->dev.of_node = pdev->dev.of_node;
 
        bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
 
index 35a473ba3d81d5afb9cb0188f2141c2dc417c737..3b9bd9a3f2b08a0a43683c87f1801d2f330185c3 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 2d46f13adfdfc965561db40bdeafa55f6c445978..ce7ffba2b0208f24fa7324c5c7ad283346e2c693 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 132369fad4e0fefe09e10dd88ade2eaf9a8b1972..af0b5830303d761378d9111ac141bda6502dd665 100644 (file)
@@ -125,12 +125,12 @@ static struct davinci_i2c_platform_data davinci_i2c_platform_data_default = {
 static inline void davinci_i2c_write_reg(struct davinci_i2c_dev *i2c_dev,
                                         int reg, u16 val)
 {
-       __raw_writew(val, i2c_dev->base + reg);
+       writew_relaxed(val, i2c_dev->base + reg);
 }
 
 static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg)
 {
-       return __raw_readw(i2c_dev->base + reg);
+       return readw_relaxed(i2c_dev->base + reg);
 }
 
 /* Generate a pulse on the i2c clock pin. */
@@ -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 5888feef1ac5a959b5f0471a3fb71b9a2b00bada..e89e3e2145e557898bd04766312a41811c3a85cf 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 0aa01136f8d955148d984ac00e06ffb9e8ce1196..d0bdac0498cebafe3def932006c1380cfc318c1f 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 dae3ddfe7619cf69899d974992ea0e7b43264b59..721f7ebf9a3bcb034867dd8acfbb33236bf21e46 100644 (file)
@@ -25,8 +25,6 @@
 #define USB_VENDOR_ID_DIOLAN           0x0abf
 #define USB_DEVICE_ID_DIOLAN_U2C       0x3370
 
-#define DIOLAN_OUT_EP          0x02
-#define DIOLAN_IN_EP           0x84
 
 /* commands via USB, must match command ids in the firmware */
 #define CMD_I2C_READ           0x01
@@ -84,6 +82,7 @@
 struct i2c_diolan_u2c {
        u8 obuffer[DIOLAN_OUTBUF_LEN];  /* output buffer */
        u8 ibuffer[DIOLAN_INBUF_LEN];   /* input buffer */
+       int ep_in, ep_out;              /* Endpoints    */
        struct usb_device *usb_dev;     /* the usb device for this device */
        struct usb_interface *interface;/* the interface for this device */
        struct i2c_adapter adapter;     /* i2c related things */
@@ -109,7 +108,7 @@ static int diolan_usb_transfer(struct i2c_diolan_u2c *dev)
                return -EINVAL;
 
        ret = usb_bulk_msg(dev->usb_dev,
-                          usb_sndbulkpipe(dev->usb_dev, DIOLAN_OUT_EP),
+                          usb_sndbulkpipe(dev->usb_dev, dev->ep_out),
                           dev->obuffer, dev->olen, &actual,
                           DIOLAN_USB_TIMEOUT);
        if (!ret) {
@@ -118,7 +117,7 @@ static int diolan_usb_transfer(struct i2c_diolan_u2c *dev)
 
                        tmpret = usb_bulk_msg(dev->usb_dev,
                                              usb_rcvbulkpipe(dev->usb_dev,
-                                                             DIOLAN_IN_EP),
+                                                             dev->ep_in),
                                              dev->ibuffer,
                                              sizeof(dev->ibuffer), &actual,
                                              DIOLAN_USB_TIMEOUT);
@@ -210,7 +209,7 @@ static void diolan_flush_input(struct i2c_diolan_u2c *dev)
                int ret;
 
                ret = usb_bulk_msg(dev->usb_dev,
-                                  usb_rcvbulkpipe(dev->usb_dev, DIOLAN_IN_EP),
+                                  usb_rcvbulkpipe(dev->usb_dev, dev->ep_in),
                                   dev->ibuffer, sizeof(dev->ibuffer), &actual,
                                   DIOLAN_USB_TIMEOUT);
                if (ret < 0 || actual == 0)
@@ -445,9 +444,14 @@ static void diolan_u2c_free(struct i2c_diolan_u2c *dev)
 static int diolan_u2c_probe(struct usb_interface *interface,
                            const struct usb_device_id *id)
 {
+       struct usb_host_interface *hostif = interface->cur_altsetting;
        struct i2c_diolan_u2c *dev;
        int ret;
 
+       if (hostif->desc.bInterfaceNumber != 0
+           || hostif->desc.bNumEndpoints < 2)
+               return -ENODEV;
+
        /* allocate memory for our device state and initialize it */
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (dev == NULL) {
@@ -455,6 +459,8 @@ static int diolan_u2c_probe(struct usb_interface *interface,
                ret = -ENOMEM;
                goto error;
        }
+       dev->ep_out = hostif->endpoint[0].desc.bEndpointAddress;
+       dev->ep_in = hostif->endpoint[1].desc.bEndpointAddress;
 
        dev->usb_dev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
index 0f3752967c4be717d3a451cda4737644939fe493..ff15ae90aaf54bb5c52585e087954338c0e40a31 100644 (file)
@@ -311,24 +311,6 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap)
        pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_START);
 }
 
-/**
- * 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 bfa02c6c2ddaf01b03a9a5121c2ddcf122f9a3e2..d9f7e186a4c7577c3f77eaf3905547f4a189a7b8 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 4296d1721272bd3fa077944a63e5708000b6a4dd..737e298668878f6b0c7d7421374248b596839668 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 1672effbcebb23894b99deac27bb84d595fa28d2..0043ede234c2106455a592d0f654b40f4325627c 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 d3e9cc3153a973dc62f99d7d607e003179f41f9d..8be7e42aa4de88ba3f00ef2275455b27fa5cdf96 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 b7c857774708d369931abb733b9675500b9138bd..0cde4e6ab2b2f127c450088c88f3775d337c6f6a 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 9967a6f9c2ffba2fe4521a8abf19b49e978110a4..90dcc2eaac5fb688fedba904b181be2312e40b3d 100644 (file)
@@ -266,13 +266,13 @@ static const u8 reg_map_ip_v2[] = {
 static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
                                      int reg, u16 val)
 {
-       __raw_writew(val, i2c_dev->base +
+       writew_relaxed(val, i2c_dev->base +
                        (i2c_dev->regs[reg] << i2c_dev->reg_shift));
 }
 
 static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
 {
-       return __raw_readw(i2c_dev->base +
+       return readw_relaxed(i2c_dev->base +
                                (i2c_dev->regs[reg] << i2c_dev->reg_shift));
 }
 
@@ -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;
@@ -1037,6 +1037,20 @@ static const struct i2c_algorithm omap_i2c_algo = {
 };
 
 #ifdef CONFIG_OF
+static struct omap_i2c_bus_platform_data omap2420_pdata = {
+       .rev = OMAP_I2C_IP_VERSION_1,
+       .flags = OMAP_I2C_FLAG_NO_FIFO |
+                       OMAP_I2C_FLAG_SIMPLE_CLOCK |
+                       OMAP_I2C_FLAG_16BIT_DATA_REG |
+                       OMAP_I2C_FLAG_BUS_SHIFT_2,
+};
+
+static struct omap_i2c_bus_platform_data omap2430_pdata = {
+       .rev = OMAP_I2C_IP_VERSION_1,
+       .flags = OMAP_I2C_FLAG_BUS_SHIFT_2 |
+                       OMAP_I2C_FLAG_FORCE_19200_INT_CLK,
+};
+
 static struct omap_i2c_bus_platform_data omap3_pdata = {
        .rev = OMAP_I2C_IP_VERSION_1,
        .flags = OMAP_I2C_FLAG_BUS_SHIFT_2,
@@ -1055,6 +1069,14 @@ static const struct of_device_id omap_i2c_of_match[] = {
                .compatible = "ti,omap3-i2c",
                .data = &omap3_pdata,
        },
+       {
+               .compatible = "ti,omap2430-i2c",
+               .data = &omap2430_pdata,
+       },
+       {
+               .compatible = "ti,omap2420-i2c",
+               .data = &omap2420_pdata,
+       },
        { },
 };
 MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
@@ -1140,9 +1162,9 @@ omap_i2c_probe(struct platform_device *pdev)
         * Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2.
         * On omap1/3/2 Offset 4 is IE Reg the bit [15:14] is 0 at reset.
         * Also since the omap_i2c_read_reg uses reg_map_ip_* a
-        * raw_readw is done.
+        * readw_relaxed is done.
         */
-       rev = __raw_readw(dev->base + 0x04);
+       rev = readw_relaxed(dev->base + 0x04);
 
        dev->scheme = OMAP_I2C_SCHEME(rev);
        switch (dev->scheme) {
index 1a9ea25f23142847e1ea57cbd53cdb39cff34322..c9a352f0a9a50cf812aa73ba168987a98ee080ea 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 d2fe11da5e82a43cdc80c9fc9446aeb1e7c7fa0b..2c2fd7c2b116624f737352fb6ea5dee55ff3cad3 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 3747b9bf67d6440f684bcff945a2a85b93f6fe6c..bf8fb94ebc5dd116eae9eca60fa7276bdbad9b9f 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 c447e8d40b78fb5cc3d58b0fb7b48ffff066c28a..599235514138564ba2db1befc6b711655420cf16 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 55110ddbed1ff4163d48acf20da36b58e2ee2e43..1d79585ba4b37f6b038b3eaf523a474d493fa320 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 c457cb447c66bdf51911339853b561c273c06082..e661edee4d0cf0d92b7ab5e6899d1993bb264799 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 c65da3d913a03d11ed13d62c17d7dffb3fb097bf..2c8a3e4f900890e11f3fd1f14c0e15aa78d2ecce 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 4c8b368d463b7c77517fb3ed314e4234bd6dafbc..fc2716afdfd91b3a053a1a37ed1db4c68be69410 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 75ba8608383e4b1d26e65a389f099f7028e7b733..d74c0b34248ea6c38472cc401571d8f519844c4d 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 c3ccdea3d18059c4dd199f533932ff17f0b4f448..80b47e8ce030cef7ff6f9ab9f058b15e63f7329e 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 44d4c6071c15096e19d16433092b7428c113f34f..c99b229873665b56656a7cabd95c7dfd6acb3fb1 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 928656e241ddc0873613dd7a5b786be4f0e5250e..c58e093b6032480a316c1725db5f6879842cd2ef 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 a764da777f08027da102e244556f9bb424b59494..8a8c56f4b026d6a22e54941e9959ea633b42c321 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 68a37157377df12797b1b122ca4568d121559112..d7978dc4ad0b075a03b842bc93b52a61c952ae70 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 24214ab60ac0b887a5262c8034d616459ac208d5..de9185db41d4e2fa786eb28618e38cccece73628 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 140c8ef505291129d6299d2b4d4931d3b728589e..d9e1f7ccfe6f086df549160a88ca191e52fca32f 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 bf83d7bb6bc647a5ab92fb2055afe0b34ad25f8b..2db803cd095cb352d8607d8503da23d8b6011727 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 3226ce98fb184df9c0c5666129c1b699cbcc5027..cbd4e9abc47e8f47f512915d224f916166921110 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 dcda17395c4e68f31f3382cd0c393a5845b025c5..1cae4e920c9ba980a45e3b17249b16ffa998f9e6 100644 (file)
@@ -350,7 +350,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
 error_iio_unreg:
        iio_device_unregister(indio_dev);
 error_remove_trigger:
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&accel_state->common_attributes);
 error_unreg_buffer_funcs:
        iio_triggered_buffer_cleanup(indio_dev);
 error_free_dev_mem:
@@ -363,10 +363,11 @@ static int hid_accel_3d_remove(struct platform_device *pdev)
 {
        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct accel_3d_state *accel_state = iio_priv(indio_dev);
 
        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
        iio_device_unregister(indio_dev);
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&accel_state->common_attributes);
        iio_triggered_buffer_cleanup(indio_dev);
        kfree(indio_dev->channels);
 
index d72118d1189c8648161496919ab17f7ad514df95..98ba761cbb9ce6943913c03b0a65d861e6956661 100644 (file)
@@ -112,9 +112,10 @@ static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
        mutex_lock(&st->buf_lock);
        st->tx[0] = KXSD9_READ(address);
        ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
-       if (ret)
-               return ret;
-       return (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
+       if (!ret)
+               ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
+       mutex_unlock(&st->buf_lock);
+       return ret;
 }
 
 static IIO_CONST_ATTR(accel_scale_available,
index e6fbd3e70981fca1a4d93d2c03891fcf1efde6c0..9a4e0e32a771c392dc153c19f9215ba7c3433d44 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 17df74908db120a6e33e3f43a51fc4e67784da59..5b1aa027c034b09c1569047231716ea66f75f919 100644 (file)
@@ -1047,6 +1047,7 @@ static int at91_adc_probe(struct platform_device *pdev)
        } else {
                if (!st->caps->has_tsmr) {
                        dev_err(&pdev->dev, "We don't support non-TSMR adc\n");
+                       ret = -ENODEV;
                        goto error_disable_adc_clk;
                }
 
index 12948325431c98a1a01effc90d1bec0f6a564b50..c8c1baaec6c1bf7b590e144c79a6b73efbf13af2 100644 (file)
@@ -88,10 +88,10 @@ static const int mcp3422_sample_rates[4] = {
 
 /* sample rates to sign extension table */
 static const int mcp3422_sign_extend[4] = {
-       [MCP3422_SRATE_240] = 12,
-       [MCP3422_SRATE_60] = 14,
-       [MCP3422_SRATE_15] = 16,
-       [MCP3422_SRATE_3] = 18 };
+       [MCP3422_SRATE_240] = 11,
+       [MCP3422_SRATE_60] = 13,
+       [MCP3422_SRATE_15] = 15,
+       [MCP3422_SRATE_3] = 17 };
 
 /* Client data (each client gets its own) */
 struct mcp3422 {
index 54c5babe67469bede8e70412470a24dd901e9000..e525aa6475c42b20cef6f3653da973620a2bccc9 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 728411ec764203c371270c390ac77176d788ffd9..d4d748214e4b364dc716889d2b66363353c2ee56 100644 (file)
@@ -229,12 +229,15 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
        unsigned long flags,
        const struct iio_buffer_setup_ops *setup_ops)
 {
+       struct iio_buffer *buffer;
        int ret;
 
-       indio_dev->buffer = iio_kfifo_allocate(indio_dev);
-       if (!indio_dev->buffer)
+       buffer = iio_kfifo_allocate(indio_dev);
+       if (!buffer)
                return -ENOMEM;
 
+       iio_device_attach_buffer(indio_dev, buffer);
+
        ret = request_threaded_irq(irq, pollfunc_th, pollfunc_bh,
                                flags, indio_dev->name, indio_dev);
        if (ret)
index b6e77e0fc420133af7a324de6a0531bfb045f33d..bbd6426c9726d8f4e0bf36f1e138901c19ff99e8 100644 (file)
@@ -55,11 +55,10 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
        return 0;
 }
 
-void hid_sensor_remove_trigger(struct iio_dev *indio_dev)
+void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
 {
-       iio_trigger_unregister(indio_dev->trig);
-       iio_trigger_free(indio_dev->trig);
-       indio_dev->trig = NULL;
+       iio_trigger_unregister(attrb->trigger);
+       iio_trigger_free(attrb->trigger);
 }
 EXPORT_SYMBOL(hid_sensor_remove_trigger);
 
@@ -90,7 +89,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
                dev_err(&indio_dev->dev, "Trigger Register Failed\n");
                goto error_free_trig;
        }
-       indio_dev->trig = trig;
+       indio_dev->trig = attrb->trigger = trig;
 
        return ret;
 
index 9a8731478eda4cdb95867e5dffdd10d1ec9bcbae..ca02f7811aa8c6f6a44dc8a955a064dfa1e07219 100644 (file)
@@ -21,6 +21,6 @@
 
 int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
                                struct hid_sensor_common *attrb);
-void hid_sensor_remove_trigger(struct iio_dev *indio_dev);
+void hid_sensor_remove_trigger(struct hid_sensor_common *attrb);
 
 #endif
index ea01c6bcfb56825979efc7bfc588eeb9e429e059..e54f0f4959d37abc28202ae75e044ec249cdd73c 100644 (file)
@@ -348,7 +348,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
 error_iio_unreg:
        iio_device_unregister(indio_dev);
 error_remove_trigger:
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&gyro_state->common_attributes);
 error_unreg_buffer_funcs:
        iio_triggered_buffer_cleanup(indio_dev);
 error_free_dev_mem:
@@ -361,10 +361,11 @@ static int hid_gyro_3d_remove(struct platform_device *pdev)
 {
        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
 
        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
        iio_device_unregister(indio_dev);
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&gyro_state->common_attributes);
        iio_triggered_buffer_cleanup(indio_dev);
        kfree(indio_dev->channels);
 
index dac15b9f9df8db9bf42518d1394875347b5fb0c6..c10eab64bc0524eca6caa476d224c370fcd49262 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 f98c2b509254e2b8fdce8cd77db408f11b244495..b0d65df3ede2050d4c9944e819b45917ef945c87 100644 (file)
@@ -81,6 +81,8 @@ config SENSORS_LM3533
 config TCS3472
        tristate "TAOS TCS3472 color light-to-digital converter"
        depends on I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
        help
         If you say yes here you get support for the TAOS TCS3472
         family of color light-to-digital converters with IR filter.
index fa6ae8cf89eaa9edfdfffe379939bc7cc8a491f0..8e8b9d72285373b2a41be93c937407d51dcc637f 100644 (file)
@@ -314,7 +314,7 @@ static int hid_als_probe(struct platform_device *pdev)
 error_iio_unreg:
        iio_device_unregister(indio_dev);
 error_remove_trigger:
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&als_state->common_attributes);
 error_unreg_buffer_funcs:
        iio_triggered_buffer_cleanup(indio_dev);
 error_free_dev_mem:
@@ -327,10 +327,11 @@ static int hid_als_remove(struct platform_device *pdev)
 {
        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct als_state *als_state = iio_priv(indio_dev);
 
        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
        iio_device_unregister(indio_dev);
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&als_state->common_attributes);
        iio_triggered_buffer_cleanup(indio_dev);
        kfree(indio_dev->channels);
 
index 0cf09637b35b64f16a37c3adece1416cc453fee1..d86d226dcd67e09b585652ef983c9ccb038cdb99 100644 (file)
@@ -19,6 +19,8 @@ config AK8975
 config MAG3110
        tristate "Freescale MAG3110 3-Axis Magnetometer"
        depends on I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
        help
          Say yes here to build support for the Freescale MAG3110 3-Axis
          magnetometer.
index 2634920562fb7263bad4ac9d8c6c75fe7a434b49..b26e1028a0a0b17ae6f0b94d936f50186b1d3f28 100644 (file)
@@ -351,7 +351,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
 error_iio_unreg:
        iio_device_unregister(indio_dev);
 error_remove_trigger:
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&magn_state->common_attributes);
 error_unreg_buffer_funcs:
        iio_triggered_buffer_cleanup(indio_dev);
 error_free_dev_mem:
@@ -364,10 +364,11 @@ static int hid_magn_3d_remove(struct platform_device *pdev)
 {
        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct magn_3d_state *magn_state = iio_priv(indio_dev);
 
        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
        iio_device_unregister(indio_dev);
-       hid_sensor_remove_trigger(indio_dev);
+       hid_sensor_remove_trigger(&magn_state->common_attributes);
        iio_triggered_buffer_cleanup(indio_dev);
        kfree(indio_dev->channels);
 
index 783c5b417356e0ecaf0d3c095f33c2b4a24fa496..becf54496967aee126fb6449b54341394645d9bd 100644 (file)
@@ -250,7 +250,12 @@ done:
        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
                BIT(IIO_CHAN_INFO_SCALE), \
        .scan_index = idx, \
-       .scan_type = IIO_ST('s', 16, 16, IIO_BE), \
+       .scan_type = { \
+               .sign = 's', \
+               .realbits = 16, \
+               .storagebits = 16, \
+               .endianness = IIO_BE, \
+       }, \
 }
 
 static const struct iio_chan_spec mag3110_channels[] = {
index b84791f03a27f5d174b60680f77c59de06bb542a..5ceda710f516bc4721e14961a504fa7e2c3054a9 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 784b97cb05b00f3f8d08786d7ef0ff22aa8434b0..f2ef7ef0f36f7c19df3a2eb53fb3e3b532716a1b 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 d2172e71f985fd9f8ec1418c1c719a5491b97a44..8e49db690f33e9a67f71cbcca8b62c9d92fde9f4 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 da06abde9e0d557ae1536f1995cd1a84af6ab219..a1e9cba849446b1cd5ac840b66b18355451377b4 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 cde1e7b5b85ddd87b1e44e9886be0be19bf3d137..faad2caf22b1e06cbe2f7e628354f16efb686d0a 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 b0f189be543bf61749bdcf37d6fd7a17f3d57fcc..ab8b1c30b36b7fb8cf70ca243a5945a14ae1e045 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 d8f9c6c272d732a898b334be0c3034aadf37b275..bdc842e9faefe4ba477b96c479975643bfdc6366 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 2f0f01b70e3bd22c538cd3a4081be0a9ec3f790f..65f6e7dc380c382cc0335e495fb2f583a4eef7b7 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 2df31f68ea0905ef06ac1ed054348cf669cfaa61..34386943ebcff4cf18849082add568f5fc73bc5a 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 a321df28bab2696404e76a365c861637867b68d2..d4f6ddf72ffa5add41bd392d6ed773386a93bdb7 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 33d2cc6ab56220bed4a49af1fb0f22e68955508d..4a033853312e52c6ff026d8e7e7892f010826255 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 f5cb13b2144566229c03be75e31623cb19942d45..cc04b7ba34882cc7981ec3815af73dc7a0519d91 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 d5e60f44ba5ad7c4f5ad53d7e0fe348248ed797b..66dbf8062374784299f5e3790738a424248116c7 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 6a0a0d29660df5fe7d3cd90bf5c74eead9da4936..1958c5ca792ad52a39ece6247040588c00b84ba6 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 344ab03948a315cafc0422863f70a6e30443ff49..b726274297458e2de91b2c324881f2f83dababf6 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 b1a6cb3a2809282fbfab1b44b09c53e315a93466..306534109627082c36e30f270b219e1a500c5590 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 836be91572424a9f9fd87f41b353b88f67706e06..4c134d93d4fc150ca668f69eb998b563ad3ff47a 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 3453580b1eb2cc45800b8ee697157a24cf269970..039c3e40fcb4c3f36d5dba3c3ef9daf0ed5c74e0 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 5659ea88074108e60d9253085ce89f962326b808..7c6b4ba49bec13c920120cb775090bd153354fb5 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 0aa478bc291ae39aec0ed8c243cce72fe7ed2bdd..210b3eaf188aa25fcf11f657cefe907c8f77f197 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 5b53ca5a22840c14e31ddb0d7e978dfac5c179b7..8308e3634767958a4a46920ca11983bf62e62e13 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 adc11d14f8783faa9afcac1ee6b6faaadcb8d267..294dd27b601e0434e251b46732c66f69cfb9d242 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 50219ab2279d56ae6599e7189b06034bf12049d9..56bf32fcb62c9b8643816503eabb1d9693220c8c 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 0ce7674621eaba2aa310fef332695b67f3057971..91443bcb9e0e89faf1a93d4dcacb22943f5e6e10 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 69f1d1221a6bea07039fc37b5ead33f941fc74a4..7686dceadd292bf92a2a1ab273bf33a918d0967e 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 016e7429adf66b9ffb945cb4fed168e358c57742..5bfc02f450e6a69251632db17fb6ff699dfe8c10 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 28874f8606f888ee0a7a816cf180b654f622bc32..941d4d50d8e74a76f333498c66ab9eb3da9b6246 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 d0a0ea0c14d6a965afe2bcf8d359052fdb946e26..165aee2ca8a0c38dfc1e00b1965172acf4f30193 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 012e2c7575ad5eb117f9542ec692f3f8d218eb71..a01c7d2cf54114def170ed8592a403b0b3d21e57 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 eb71aaa26a9a9345f5f4998cd30eb4d0a7f01f2f..c639f90cfda41709ce77c4051b8ed8086f4dd700 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 7a3175400b2a1c6cf05b85e59f39773b5eb4db86..1377f85911c2490dc62764452856bcf680af7959 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 196b1d13cbcbc09548e92a4be6c9a18cdc6aae36..6a7003ddb0be19e665220e843391bd148eb8ded4 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 82cec1af902cd24533ef032869cf2052fa9d4f63..d64ed05fb082f6a538b4c774c9f93a54940557ee 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 cecb98a4c662b7d82053f152a3da46d653e4d6ca..d4e005720d0181729096b4fb8a81aa21498f023b 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 f81abe16cf093d6c74ad31dd179f03f7dc90b997..c29b5c8388331458bb2a2caf85580d76031f1dcb 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 8292554bccb5de2387d7e858eaaa1d27ddb6b8b4..9fad7b5ac8b91910af7e6b1527f20165323e7dc8 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 ce3fd32167dcdc68c609ef9852263df65fd0b5c1..02f9759ebb1a573677993c7b17741e851eb591f7 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 6df23502059a44eccd32c725477cd5fee1ef2923..6be57c38638d28dd0a39464bffe391c4a093f478 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;
@@ -1623,6 +1643,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)
 {
@@ -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 631f2090f0b8cfde2db02d17c9162d3e97558298..691f90ff2d83603541d357d3b4f09e014e93e61a 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 f93baf8254c4d492065155b4b701529629260b30..a88631918e852880731c3305b009b53f0fbc9d48 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 e641088c14dc37bda98e95134b4235f151e3c0fc..575681063f38b11fac26815e7c5e2a1fe6db5b42 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 6c923c7039a156d10eeaa7a39f339ecd84a360ac..520a7e5a490b1b61042cad7acead955bbed2b759 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 38b523a1ece0634e7855b5eae62b94b6fe7fd160..a11ff74a5127019cb6f367d07ffae32429ccf37e 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 b6ded17b3be3adda86ca93bf582f679f837442aa..a06e12552886fa57ffbe1731eb744762666cd787 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 e75d015024a15c7fb6eb3074299e234a69916594..846ccdd905b19b66872762fd05db29ea0552a8cb 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 c1edd39bc5ba1c1c39c81cd895f3e3ab8cbb5c01..bb174c1a9886eb8a1592f347e71e20bdb6fd90b6 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 440ce32462baefafcabdda55c5c168c27753757c..2db13246eb8e0ba360a9cddc8c7d82b842f62ff8 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 cd5ed9e221686625ba2ef92916c155c84732cb81..4e428199e580f36c1c9228870d8b4b1a8d02ef0f 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 42181435fe6759f4302ac8ffdd68f0ea41d1df0f..8b1b01361ec6feb9f9670713717f72e0a57a63ff 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 b3e3edab6d9f208050bf029a85c0b63ab755969b..b31064981e9687d0c448c8b7410bbf802d256cce 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 a2e758d27584c0117a5c941d36b8042ab20f425f..186138c720c79ebfee4ef896c61aeee78c770289 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 9cd20e6905a08274bff3d55a48fa7e26d8f2b2c6..8508879f6fafd2663d393cb6afad9f00340b341c 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 5f7b427dd7ed06e48b53aa6f0ae6c236af82dcfa..8bd24d52bf1bedd0438e0737350c640b6e3b38b8 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 aa51baaa9b1ec273acc95c7edcb838ed81fe5fe2..5f4967d01bc36a621655f64eea7e2c1408ef43b7 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 61891486067cab2a585303398182ffb721688658..3a90b710e30960e95421c1d486058ade968fd6bc 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 4f77f87847e805ef33abf52f72f21a2dd92b2c81..b5d71d245854684a33d8baeead24b78148a087c7 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 86b822806e95b504f9f4b23303365cff0d6b6a1d..45e0e3e55de28dfdbdfb347ebdba74ca2e698544 100644 (file)
@@ -180,7 +180,10 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
        if (WARN_ON(down_interruptible(&i8042tregs)))
                return -1;
 
-       if (hp_sdc_enqueue_transaction(&t)) return -1;
+       if (hp_sdc_enqueue_transaction(&t)) {
+               up(&i8042tregs);
+               return -1;
+       }
        
        /* Sleep until results come back. */
        if (WARN_ON(down_interruptible(&i8042tregs)))
index f3309696d053ae0692a21e39766806fd62ab9957..59d4dcddf6de0cf4d1c5384c977a36c319d66389 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 dce0d95943c537e1041dbca8372113f87fab3efb..6983ffbbfb94ac5fa90e0147c6a19de44d32e98b 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 2ff4d1c78ab8ff91c4338c6e40d6ff91fac349c3..940566e7be1322751689027166653845173d6004 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 fb4f8ac3343bd5c47c987e4ffb11c2540cf60267..83fff38b86b367ccdd2728095cafc769b6d00531 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 5b1aff825138a1b9db5d2b216f7389168bb7bf9f..f920ba7ab51f5e1399649fa75c158a3770ae86e7 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 0621c367049a3e8a0183af7f9d50611c3c0f3a11..7b8b03e0d0bef905cd793a158cfaba2f135eeb5f 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 a0a4bbaef02c242a8ee16e54bdb9337e8f80407e..772835938a526ed2575f6bc187619fe962ac0e6c 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 83658472ad2511735b2e02c8ee645858385d58f0..ca7a26f1dce81c37c6cd8524f8c4698effbfd739 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 f51765fff0545e91be567942255cf19532f94a71..a5869a856ea5ca3fff3a0c5bda49d0f81132b640 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 1de1e5f8f7956dad40a745a08c7d4026f1370e28..8541f949778dc16953bdd269418113f0aa132e04 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 12298b1c0e716b62a771b9255b9f4f5c24b9e08e..815d874fe72469bd49a93edda8ea28c6aa3c7e58 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 5f306f79da0cecaebd986f0fe39595c451b2ad9b..0ec9abbe31fec3af5248808fce517fc863ff75b2 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 52c9ebf94729ff5bf6531cc7773902ece632d03b..020053fa5aaa38fce82b5fc2d6ced8546661c9df 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 e53416a4d7f3275ea2fd8f53f6a55dcbb6de02ad..867e7c33ac55072b19f2ff339705c4e90f6b428d 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 c59b797eeafaa4f6258552389437cb8d113e1642..782c2535f1d81a26db96716ac406405b2b03a78b 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 dfc9e08e7f70458c0a41378ae7034a027e52d0ea..fd23a3790605070a41ec37f19eff051b0e89be5a 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 e09ec67957a3c32b185dd451fedba467729d0b77..961d58d3264769fb3daec51182effd8742bd1b5e 100644 (file)
@@ -906,6 +906,17 @@ config TOUCHSCREEN_STMPE
          To compile this driver as a module, choose M here: the
          module will be called stmpe-ts.
 
+config TOUCHSCREEN_SUR40
+       tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
+       depends on USB
+       select INPUT_POLLDEV
+       help
+         Say Y here if you want support for the Samsung SUR40 touchscreen
+         (also known as Microsoft Surface 2.0 or Microsoft PixelSense).
+
+         To compile this driver as a module, choose M here: the
+         module will be called sur40.
+
 config TOUCHSCREEN_TPS6507X
        tristate "TPS6507x based touchscreens"
        depends on I2C
@@ -919,4 +930,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 f5216c1bf53e7faa637f34e3570f8d9ba104c291..62801f213346a8e358af358e409e0768130744bc 100644 (file)
@@ -54,6 +54,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR)      += pixcir_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)      += s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)       += st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)                += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SUR40)                += sur40.o
 obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)        += ti_am335x_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_TNETV107X)    += tnetv107x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
@@ -75,3 +76,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 f3a174a83c82ced113c265ba1b1601774f23e765..69834dd3c313b95dcab54dcd23d51a6730727868 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 606da5bd61150958afca42a472c230a6e70ad26e..1a7b1143536e1d4a68dd6fb9e1c85cf992afee04 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 268a35e55d7f160fd632a673276f67ee6672665f..279c0e42b8a7515a161550621ac82dd2ccd7d0f0 100644 (file)
@@ -391,7 +391,7 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int atmel_wm97xx_suspend(struct *dev)
+static int atmel_wm97xx_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
index d038575f49db3a6d70e16ee640fb67cfbcab24be..a035a390f8e29f4caa36e4949fd54d469e9cdc24 100644 (file)
@@ -1246,8 +1246,7 @@ static void cyttsp4_watchdog_timer(unsigned long handle)
 
        dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__);
 
-       if (!work_pending(&cd->watchdog_work))
-               schedule_work(&cd->watchdog_work);
+       schedule_work(&cd->watchdog_work);
 
        return;
 }
@@ -2113,7 +2112,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 +2149,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 a71e1141d6386f9546fe32e5496437e72e393885..b19434cebbf6ac56d18ff13fc408108d119d7843 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 d53e0b72a4078aadcb05ab43c319fce8b786da76..4204841cdc49743054f284cd4fcae8b7131255e8 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 ef5fcb0945e910b11918b26159a2860cec2cba2a..054d22583248386a5d392c0df90fed81d950e083 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 66500852341b66319b634133b3f0930f3dc3f2ec..92e2243fb77d9a3b1b3e13316ea38e0072aed7aa 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 1740a2496371d5988d2d550a4312988bafcf850b..2f03b2f289dd365fe3053338d09d049b7d724244 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>
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
new file mode 100644 (file)
index 0000000..cfd1b7e
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Surface2.0/SUR40/PixelSense input driver
+ *
+ * Copyright (c) 2013 by Florian 'floe' Echtler <floe@butterbrot.org>
+ *
+ * Derived from the USB Skeleton driver 1.1,
+ * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * and from the Apple USB BCM5974 multitouch driver,
+ * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
+ *
+ * and from the generic hid-multitouch driver,
+ * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/printk.h>
+#include <linux/input-polldev.h>
+#include <linux/input/mt.h>
+#include <linux/usb/input.h>
+
+/* read 512 bytes from endpoint 0x86 -> get header + blobs */
+struct sur40_header {
+
+       __le16 type;       /* always 0x0001 */
+       __le16 count;      /* count of blobs (if 0: continue prev. packet) */
+
+       __le32 packet_id;  /* unique ID for all packets in one frame */
+
+       __le32 timestamp;  /* milliseconds (inc. by 16 or 17 each frame) */
+       __le32 unknown;    /* "epoch?" always 02/03 00 00 00 */
+
+} __packed;
+
+struct sur40_blob {
+
+       __le16 blob_id;
+
+       u8 action;         /* 0x02 = enter/exit, 0x03 = update (?) */
+       u8 unknown;        /* always 0x01 or 0x02 (no idea what this is?) */
+
+       __le16 bb_pos_x;   /* upper left corner of bounding box */
+       __le16 bb_pos_y;
+
+       __le16 bb_size_x;  /* size of bounding box */
+       __le16 bb_size_y;
+
+       __le16 pos_x;      /* finger tip position */
+       __le16 pos_y;
+
+       __le16 ctr_x;      /* centroid position */
+       __le16 ctr_y;
+
+       __le16 axis_x;     /* somehow related to major/minor axis, mostly: */
+       __le16 axis_y;     /* axis_x == bb_size_y && axis_y == bb_size_x */
+
+       __le32 angle;      /* orientation in radians relative to x axis -
+                             actually an IEEE754 float, don't use in kernel */
+
+       __le32 area;       /* size in pixels/pressure (?) */
+
+       u8 padding[32];
+
+} __packed;
+
+/* combined header/blob data */
+struct sur40_data {
+       struct sur40_header header;
+       struct sur40_blob   blobs[];
+} __packed;
+
+
+/* version information */
+#define DRIVER_SHORT   "sur40"
+#define DRIVER_AUTHOR  "Florian 'floe' Echtler <floe@butterbrot.org>"
+#define DRIVER_DESC    "Surface2.0/SUR40/PixelSense input driver"
+
+/* vendor and device IDs */
+#define ID_MICROSOFT 0x045e
+#define ID_SUR40     0x0775
+
+/* sensor resolution */
+#define SENSOR_RES_X 1920
+#define SENSOR_RES_Y 1080
+
+/* touch data endpoint */
+#define TOUCH_ENDPOINT 0x86
+
+/* polling interval (ms) */
+#define POLL_INTERVAL 10
+
+/* maximum number of contacts FIXME: this is a guess? */
+#define MAX_CONTACTS 64
+
+/* control commands */
+#define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */
+#define SUR40_UNKNOWN1    0xb3 /*  5 bytes           */
+#define SUR40_UNKNOWN2    0xc1 /* 24 bytes           */
+
+#define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
+#define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
+
+/*
+ * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
+ * here by mistake which is very likely to have corrupted the firmware EEPROM
+ * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
+ * Should you ever run into a similar problem, the background story to this
+ * incident and instructions on how to fix the corrupted EEPROM are available
+ * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
+*/
+
+struct sur40_state {
+
+       struct usb_device *usbdev;
+       struct device *dev;
+       struct input_polled_dev *input;
+
+       struct sur40_data *bulk_in_buffer;
+       size_t bulk_in_size;
+       u8 bulk_in_epaddr;
+
+       char phys[64];
+};
+
+static int sur40_command(struct sur40_state *dev,
+                        u8 command, u16 index, void *buffer, u16 size)
+{
+       return usb_control_msg(dev->usbdev, usb_rcvctrlpipe(dev->usbdev, 0),
+                              command,
+                              USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+                              0x00, index, buffer, size, 1000);
+}
+
+/* Initialization routine, called from sur40_open */
+static int sur40_init(struct sur40_state *dev)
+{
+       int result;
+       u8 buffer[24];
+
+       /* stupidly replay the original MS driver init sequence */
+       result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);
+       if (result < 0)
+               return result;
+
+       result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12);
+       if (result < 0)
+               return result;
+
+       result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12);
+       if (result < 0)
+               return result;
+
+       result = sur40_command(dev, SUR40_UNKNOWN2,    0x00, buffer, 24);
+       if (result < 0)
+               return result;
+
+       result = sur40_command(dev, SUR40_UNKNOWN1,    0x00, buffer,  5);
+       if (result < 0)
+               return result;
+
+       result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12);
+
+       /*
+        * Discard the result buffer - no known data inside except
+        * some version strings, maybe extract these sometime...
+        */
+
+       return result;
+}
+
+/*
+ * Callback routines from input_polled_dev
+ */
+
+/* Enable the device, polling will now start. */
+static void sur40_open(struct input_polled_dev *polldev)
+{
+       struct sur40_state *sur40 = polldev->private;
+
+       dev_dbg(sur40->dev, "open\n");
+       sur40_init(sur40);
+}
+
+/* Disable device, polling has stopped. */
+static void sur40_close(struct input_polled_dev *polldev)
+{
+       struct sur40_state *sur40 = polldev->private;
+
+       dev_dbg(sur40->dev, "close\n");
+       /*
+        * There is no known way to stop the device, so we simply
+        * stop polling.
+        */
+}
+
+/*
+ * This function is called when a whole contact has been processed,
+ * so that it can assign it to a slot and store the data there.
+ */
+static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input)
+{
+       int wide, major, minor;
+
+       int bb_size_x = le16_to_cpu(blob->bb_size_x);
+       int bb_size_y = le16_to_cpu(blob->bb_size_y);
+
+       int pos_x = le16_to_cpu(blob->pos_x);
+       int pos_y = le16_to_cpu(blob->pos_y);
+
+       int ctr_x = le16_to_cpu(blob->ctr_x);
+       int ctr_y = le16_to_cpu(blob->ctr_y);
+
+       int slotnum = input_mt_get_slot_by_key(input, blob->blob_id);
+       if (slotnum < 0 || slotnum >= MAX_CONTACTS)
+               return;
+
+       input_mt_slot(input, slotnum);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
+       wide = (bb_size_x > bb_size_y);
+       major = max(bb_size_x, bb_size_y);
+       minor = min(bb_size_x, bb_size_y);
+
+       input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+       input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+       input_report_abs(input, ABS_MT_TOOL_X, ctr_x);
+       input_report_abs(input, ABS_MT_TOOL_Y, ctr_y);
+
+       /* TODO: use a better orientation measure */
+       input_report_abs(input, ABS_MT_ORIENTATION, wide);
+       input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+       input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+}
+
+/* core function: poll for new input data */
+static void sur40_poll(struct input_polled_dev *polldev)
+{
+
+       struct sur40_state *sur40 = polldev->private;
+       struct input_dev *input = polldev->input;
+       int result, bulk_read, need_blobs, packet_blobs, i;
+       u32 packet_id;
+
+       struct sur40_header *header = &sur40->bulk_in_buffer->header;
+       struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0];
+
+       dev_dbg(sur40->dev, "poll\n");
+
+       need_blobs = -1;
+
+       do {
+
+               /* perform a blocking bulk read to get data from the device */
+               result = usb_bulk_msg(sur40->usbdev,
+                       usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr),
+                       sur40->bulk_in_buffer, sur40->bulk_in_size,
+                       &bulk_read, 1000);
+
+               dev_dbg(sur40->dev, "received %d bytes\n", bulk_read);
+
+               if (result < 0) {
+                       dev_err(sur40->dev, "error in usb_bulk_read\n");
+                       return;
+               }
+
+               result = bulk_read - sizeof(struct sur40_header);
+
+               if (result % sizeof(struct sur40_blob) != 0) {
+                       dev_err(sur40->dev, "transfer size mismatch\n");
+                       return;
+               }
+
+               /* first packet? */
+               if (need_blobs == -1) {
+                       need_blobs = le16_to_cpu(header->count);
+                       dev_dbg(sur40->dev, "need %d blobs\n", need_blobs);
+                       packet_id = header->packet_id;
+               }
+
+               /*
+                * Sanity check. when video data is also being retrieved, the
+                * packet ID will usually increase in the middle of a series
+                * instead of at the end.
+                */
+               if (packet_id != header->packet_id)
+                       dev_warn(sur40->dev, "packet ID mismatch\n");
+
+               packet_blobs = result / sizeof(struct sur40_blob);
+               dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs);
+
+               /* packets always contain at least 4 blobs, even if empty */
+               if (packet_blobs > need_blobs)
+                       packet_blobs = need_blobs;
+
+               for (i = 0; i < packet_blobs; i++) {
+                       need_blobs--;
+                       dev_dbg(sur40->dev, "processing blob\n");
+                       sur40_report_blob(&(inblob[i]), input);
+               }
+
+       } while (need_blobs > 0);
+
+       input_mt_sync_frame(input);
+       input_sync(input);
+}
+
+/* Initialize input device parameters. */
+static void sur40_input_setup(struct input_dev *input_dev)
+{
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_ABS, input_dev->evbit);
+
+       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+                            0, SENSOR_RES_X, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+                            0, SENSOR_RES_Y, 0, 0);
+
+       input_set_abs_params(input_dev, ABS_MT_TOOL_X,
+                            0, SENSOR_RES_X, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_TOOL_Y,
+                            0, SENSOR_RES_Y, 0, 0);
+
+       /* max value unknown, but major/minor axis
+        * can never be larger than screen */
+       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+                            0, SENSOR_RES_X, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+                            0, SENSOR_RES_Y, 0, 0);
+
+       input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+       input_mt_init_slots(input_dev, MAX_CONTACTS,
+                           INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+}
+
+/* Check candidate USB interface. */
+static int sur40_probe(struct usb_interface *interface,
+                      const struct usb_device_id *id)
+{
+       struct usb_device *usbdev = interface_to_usbdev(interface);
+       struct sur40_state *sur40;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       struct input_polled_dev *poll_dev;
+       int error;
+
+       /* Check if we really have the right interface. */
+       iface_desc = &interface->altsetting[0];
+       if (iface_desc->desc.bInterfaceClass != 0xFF)
+               return -ENODEV;
+
+       /* Use endpoint #4 (0x86). */
+       endpoint = &iface_desc->endpoint[4].desc;
+       if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
+               return -ENODEV;
+
+       /* Allocate memory for our device state and initialize it. */
+       sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL);
+       if (!sur40)
+               return -ENOMEM;
+
+       poll_dev = input_allocate_polled_device();
+       if (!poll_dev) {
+               error = -ENOMEM;
+               goto err_free_dev;
+       }
+
+       /* Set up polled input device control structure */
+       poll_dev->private = sur40;
+       poll_dev->poll_interval = POLL_INTERVAL;
+       poll_dev->open = sur40_open;
+       poll_dev->poll = sur40_poll;
+       poll_dev->close = sur40_close;
+
+       /* Set up regular input device structure */
+       sur40_input_setup(poll_dev->input);
+
+       poll_dev->input->name = "Samsung SUR40";
+       usb_to_input_id(usbdev, &poll_dev->input->id);
+       usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
+       strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
+       poll_dev->input->phys = sur40->phys;
+       poll_dev->input->dev.parent = &interface->dev;
+
+       sur40->usbdev = usbdev;
+       sur40->dev = &interface->dev;
+       sur40->input = poll_dev;
+
+       /* use the bulk-in endpoint tested above */
+       sur40->bulk_in_size = usb_endpoint_maxp(endpoint);
+       sur40->bulk_in_epaddr = endpoint->bEndpointAddress;
+       sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL);
+       if (!sur40->bulk_in_buffer) {
+               dev_err(&interface->dev, "Unable to allocate input buffer.");
+               error = -ENOMEM;
+               goto err_free_polldev;
+       }
+
+       error = input_register_polled_device(poll_dev);
+       if (error) {
+               dev_err(&interface->dev,
+                       "Unable to register polled input device.");
+               goto err_free_buffer;
+       }
+
+       /* we can register the device now, as it is ready */
+       usb_set_intfdata(interface, sur40);
+       dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
+
+       return 0;
+
+err_free_buffer:
+       kfree(sur40->bulk_in_buffer);
+err_free_polldev:
+       input_free_polled_device(sur40->input);
+err_free_dev:
+       kfree(sur40);
+
+       return error;
+}
+
+/* Unregister device & clean up. */
+static void sur40_disconnect(struct usb_interface *interface)
+{
+       struct sur40_state *sur40 = usb_get_intfdata(interface);
+
+       input_unregister_polled_device(sur40->input);
+       input_free_polled_device(sur40->input);
+       kfree(sur40->bulk_in_buffer);
+       kfree(sur40);
+
+       usb_set_intfdata(interface, NULL);
+       dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC);
+}
+
+static const struct usb_device_id sur40_table[] = {
+       { USB_DEVICE(ID_MICROSOFT, ID_SUR40) },  /* Samsung SUR40 */
+       { }                                      /* terminating null entry */
+};
+MODULE_DEVICE_TABLE(usb, sur40_table);
+
+/* USB-specific object needed to register this driver with the USB subsystem. */
+static struct usb_driver sur40_driver = {
+       .name = DRIVER_SHORT,
+       .probe = sur40_probe,
+       .disconnect = sur40_disconnect,
+       .id_table = sur40_table,
+};
+
+module_usb_driver(sur40_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
index 24e625c0b5314217accaee7886f2d05e2652901d..68beadaabcebb134adc4ad1e9a9a8fdc6ec590d6 100644 (file)
@@ -354,8 +354,15 @@ 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;
 
@@ -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 7213e8b07e79bfa48d8b0e5d1f89d5684fee711c..811353353917d2b5f0440b8c5a5f5a704cd6c1e9 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 721fdb3597ca9d9ecb3f971f389f6852395ff39c..ae4b6b9036292c23387fafb1124247fc377c3f72 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 c880ebaf155372eaa7460491258502c9c2d35dcd..3e7fdbb4916b1c810314acb77fe9f3b0a2c7cdf1 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 14c1f474cf1188008316e7e480c7e3c6d22f04f5..5d58bf16e9e3c15d013c3cb071019be5403223df 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 181c9ba929cdfa131123639862cdefb2587c8cf6..1abfb5684ab7ebcb7c735e10c71625439490ca09 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 9009469502307d720483282f46625b8ad919caa9..8b452c9676d968ca5650b1ad8ce2128571014ba8 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 15e9b57e9cf05ba43e19d76f37e64275d3e6db44..43b9bfea48fa4d7351e22134601338426b57c6cf 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 ab86902fd9fff49e1d72f6cb81574d7c9aac6f22..bab10b1002fbf04bae1fe8e2b36cbb6f280dea51 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 fbe9ca734f8fe3839a30dcf805b159faad3cc225..e5555fcfe703b702088873a0dd648eaf19d1676e 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 108c0e9c24d97bfba39f701de0c14323ad5e3569..dba1a9fd507058712114799dae1c0bfcf2b6a1a8 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 e0665603afd9379f0dfc37dc31ee60113a82ca12..605b5b46a90390b3b2f630b5b2bcee146b4ead13 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 9031171c141b52c5e9175fdbf6eec9bd0c4224b3..341c6016812de0e17fbd4c1601708723409351c5 100644 (file)
@@ -957,12 +957,13 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
        if (WARN_ON(!gic->domain))
                return;
 
+       if (gic_nr == 0) {
 #ifdef CONFIG_SMP
-       set_smp_cross_call(gic_raise_softirq);
-       register_cpu_notifier(&gic_cpu_notifier);
+               set_smp_cross_call(gic_raise_softirq);
+               register_cpu_notifier(&gic_cpu_notifier);
 #endif
-
-       set_handle_irq(gic_handle_irq);
+               set_handle_irq(gic_handle_irq);
+       }
 
        gic_chip.flags |= gic_arch_extn.flags;
        gic_dist_init(gic);
index baf2686aa8eb0cb027044e815dafa8b2aa6afae4..02125e6a91093e22bda1849bad209f6d3ebe8962 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 e47dcb9d1e91d0ea3e383cdf8b8b09dcde2fce0a..5cefb479c7072359c5c7289d68063925da39797c 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 b3256ff0d426a2598c6e17c9e3d38cbb7e7b6036..d0a1d8a45c815e347e249beaff4855455e1e708c 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 516923926335285ce5628c205291eb22859952f3..922a1acbf652b9376e4b2dd39ba6be2859307594 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 6753b65f8edeb2db67625a6fdf468b7c8b634928..d2f0120bc878379f460fc9a9d0416d2ae2e89bdd 100644 (file)
@@ -40,6 +40,7 @@ obj-$(CONFIG_WINDFARM_RM31)     += windfarm_fcu_controls.o \
                                   windfarm_ad7417_sensor.o \
                                   windfarm_lm75_sensor.o \
                                   windfarm_lm87_sensor.o \
+                                  windfarm_max6690_sensor.o \
                                   windfarm_pid.o \
                                   windfarm_cpufreq_clamp.o \
                                   windfarm_rm31.o
index f950c9d29f3e5400a97b7eb43400d6e2a79b9e19..2638417b19aa74d1207de209b93bbb1dfc2689e8 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 e45f5575fd4dde8d2768daaa553ab57b9d0023b1..2b46bf1d7e40ca7375517ebb97a2225475844a91 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 0f12382aa35d6c939b53967b205639134e181eae..4beb55a0ff30dc9da10c340270e9cbd1c0e1c0b0 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 22d1ae72c2826a53b67656a59ea717a62a3b95a4..7d388b8bb50e35cf154d7c082ea492a5de08235d 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 ae115a253d7312f81422a4b162e2ca4952556a44..1d3c24f9fa0e95fbb20c03fcb825559b0736de1b 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 f42fc7ed9cd63b14fd4cf54a879dfd9d046584d1..5e2765aadce174e9b7cffd479667a2c48bcf500f 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 3333d3723633c424e0a22d51d7475969392c1130..767e755708964ce82f36dc88e28281b5c1b90177 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 9aba2017f0d1685ac5858ccf716c1f829681c853..dfff2410322e70263ac63e6dbfd9536bd4a5dc54 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 00039924ea9dde4ff64f7510a44202d1a1330be0..9762f1be3304f1349cc21f001cfa02d5401470e9 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);
@@ -497,40 +419,6 @@ static inline void __closure_start_sleep(struct closure *cl)
                atomic_add(CLOSURE_SLEEPING, &cl->remaining);
 }
 
-/**
- * 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.
  */
@@ -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 88e6411eab4f15829ba22c535a7b100b370bc5a6..264fcfbd629016aa1ab890cce56de2699c49be70 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 1c39b5a2489bd1158782f0afb1a28ef4fcb0a7a4..2ede60e3187475d114004111ef70c2ebaf4f38af 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 8435f81e5d858012e8aca6be8204e923a34b1d01..ecdaa671bd50457bf38d1cf9f896ffd1c8352546 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 3d7851274b0413d28d89dec4b82693b760982e39..a6472fda94b25c00a340bcfefd48b3ac0c762c6e 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 1a3b4f4786c3cee411d16c3a9a453e1fcd33b681..7c1275e66025b691ec8ee4896448d46e28a2c5d9 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 2a7f0dd6abab4d6bb21ba028b5b3a25721080a2c..fbcc851ed5a5a74b09d763c455d132dee68e7eb3 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 57dc4784f4f48c06b6816232d352d4992e45cee1..2cd65bf073c24689542d78f0ef316f81ed21ba1c 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 b8730e714d6930d77eb5ada556b9e4a341d46ab6..84d0782f702eac3e09d790077a4e353fa6a265e7 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 c7c7a8fd29fe4ba1377420d1e7f51d4aa0c57d5c..adbff141c88786c0dd7c3926d682322dd7dfb6ec 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 547c4c57b052efbb6fcd3df67c4b52c505023606..dec15cd2d797eaaa7fa0585abf8867c9f70a4cef 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 924dcfdae11102256e1ce193eefc01d82cc173cc..80d4c2bee18aa3fd1af0f58c050145af8e0a1c98 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 f7b6c197f90f94f6aeb8ec59159265962af663c1..adbc3df17a8063933fd395de7763c5e9d560ea67 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 420dad545c7d8a01e8b5c18d26db4b25677fd334..462214eeacbedbd9548e725be2e625723cda0d7e 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 ea345c6896f47777942b64f88810c99d4cbc278e..362c4b3f8b4a00e70d2e36af9ee7ec8cbe67e39e 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 ba3ee48320f2a38509adb2603f766c55e67f1da1..99053b1251bea1049c627580f3614d1c18f89b60 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 c91f61bb95b60bc95b4f5462602c3e0cd357ccec..c9ddcf4614b9300701c9867033c82bc13cadf472 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 50ea7ed24dceb79b9813857f67ddafaeb01e7ce7..81b0fa66045204604a979fdc929e723f6c914a5c 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 8766eabb0014a075e5ace35860806fb3bf31be3f..e60cebf3f519841d81726f1aee6ef97df6e8da6b 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);
 
@@ -7751,7 +7777,7 @@ void md_check_recovery(struct mddev *mddev)
        if (mddev->ro && !test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
                return;
        if ( ! (
-               (mddev->flags & ~ (1<<MD_CHANGE_PENDING)) ||
+               (mddev->flags & MD_UPDATE_SB_FLAGS & ~ (1<<MD_CHANGE_PENDING)) ||
                test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
                test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
                (mddev->external == 0 && mddev->safemode == 1) ||
@@ -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 af6681b19776d2f695030452a9d6ab42821ed0df..1e5a540995e932852df5ff484a96bfcc8636a432 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 0ff3715fb7eba5ec4fed61a9922276b07b363aff..9bebca7bff2fbc4ec4780031190e6666f7abf56d 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 7c3508abb5e178fe310cae9d2c98352efb34f2af..c504e8389e69e3ab9ad717b9f83d0c19c2008d68 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 f8b9068439267a3539d671e6e59ceb0b47b90547..cc055da02e2a300706548041dc4d9cf63957978c 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,57 +649,59 @@ 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);
                } else {
+                       spin_lock(&conf->device_lock);
                        if (atomic_read(&sh->count)) {
                                BUG_ON(!list_empty(&sh->lru)
                                    && !test_bit(STRIPE_EXPANDING, &sh->state)
                                    && !test_bit(STRIPE_ON_UNPLUG_LIST, &sh->state)
-                                   && !test_bit(STRIPE_ON_RELEASE_LIST, &sh->state));
+                                       );
                        } else {
                                if (!test_bit(STRIPE_HANDLE, &sh->state))
                                        atomic_inc(&conf->active_stripes);
-                               if (list_empty(&sh->lru) &&
-                                   !test_bit(STRIPE_EXPANDING, &sh->state))
-                                       BUG();
+                               BUG_ON(list_empty(&sh->lru));
                                list_del_init(&sh->lru);
                                if (sh->group) {
                                        sh->group->stripes_cnt--;
                                        sh->group = NULL;
                                }
                        }
+                       spin_unlock(&conf->device_lock);
                }
        } while (sh == NULL);
 
        if (sh)
                atomic_inc(&sh->count);
 
-       spin_unlock_irq(&conf->device_lock);
+       spin_unlock_irq(conf->hash_locks + hash);
        return sh;
 }
 
@@ -772,7 +857,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 +1681,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 +1697,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 +1710,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 +1728,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 +1792,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 +1832,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 +1913,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 +1931,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 +2039,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 +4021,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 +4030,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 +4050,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 +4380,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 +4391,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 +4409,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 +4437,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 +4825,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 +4920,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 +4931,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 +5098,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 +5157,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 +5199,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 +5209,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 +5223,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 +5260,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 +5370,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 +5397,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 +5439,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 +5630,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 +5667,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 +5682,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 +5707,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 +5762,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 +5770,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 +6577,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 +6676,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 2113ffa82c7a2506d21ce36076c3f6bd057d922b..01ad8ae8f57830a04de4e1e30b731f74660bda62 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 f2199e43e803594253a1ad655d93b196ecffb843..185c285f70fc99fc2b1bcf36b897c6158b8fa8d8 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 bb6ee5191eb183a415c6de0350a3ae02877a3fd2..34b0d0ddeef3f4003928487420e22aac5fb39d73 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 a142f7942a015c86da5a72f7401232c58a0ffe0f..050984c5b1e3701aa00ea0d074bca9700754d9d3 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 63676a8b024c874ab20c95862b5b1140e02d73b1..85151efdd94c75d96db013151ba6b6d2c0d04841 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 3485655fa08297c1ea6d75e73e40c3b1797e676d..58de4410c5258a7d6009ae47b5becd7e20fa6208 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 0e2ec6f73b0525583e734dcac3933da97dee8fe6..bddbab43a2df01a3f243bb5300708c09e7cf4c21 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 cebc0faffab590e3404e906eb09839f3eeb5f21a..f9cb43d9aed920816413d95656a83d3dad30a096 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 a204f2828820412f94152928f9ba20e9126b25e6..fb504f1e912500c3f8c04a9e1e0f0472255597a6 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 a777b4b944eb368f17ebe360ae37b989c2718e19..30ee59052157815edd50669afe2293ed4e2e8867 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 1b77909c0c7116c3cff03b6788912a29c2f240f3..39a29dd29519cba8573fd7ce1837e46d7df1f73b 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 0cd6927e654c110b804def49677b71f8b26cc1a9..95b981cd7115b451d7a52bd8c0da15118a6f582f 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 a771da3e9f99afc53d8c21179417df8e2feb539c..72fb5838cae0d7aec7f51bd83bebe5b0afe459b6 100644 (file)
@@ -739,7 +739,7 @@ static int cx24123_set_voltage(struct dvb_frontend *fe,
                return 0;
        default:
                return -EINVAL;
-       };
+       }
 
        return 0;
 }
index 7ca5c69dd2007202c2f3492615019f0055393558..03930d5e9fea4cbf2a5787aad71d08af809544fa 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 6201c59a78dd6ad24de4b27ee191b79c4aafdab7..e540cfb13bac4b15dbfa06d1c050e0ad1246fcd0 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 9a2134792cfae676d29a02e57acdc58d7c2d34de..959ae36403b827263d6b028f919f2fe9fe2fa224 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 082014de6875ed26a800bc15844bde5a0199c7f3..d416c15691dadd69b49eda6f26c70892c3a02652 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 c1c3400b2173508d3ce2ba77b236d16c2eec48c9..cadcae4cff89122dbf90cad2d02cfa9fa13854b4 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 ec388c1d6913c73c9458d43695793ff833fcbd67..a74ac0ddb83310ab619870a0f465771ca451eb7a 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 8e288940a61fcb0654034dde41457de506e25a8f..fbca9856313a8b8a0cfb9fe34705c1f350e31a3e 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 362d26d11e82a4d1f975db097679c7d4bb7ddb97..7efb796c472c475bed62f1a0bb783f5b991a5b1e 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 facb84841518e66cd274308af34009a16a8a7390..ff73da9365e3c7083a6dea2343f0de0bb27a3bb6 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 91b2dcf5a6eaa429366de319ad58c405d708b398..2cfbb6a97061b166bac354c964a64a0e5a227eaa 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 e2fec9ebf947d6b5c0ca03eeb50befd233f6f19a..93eeaf7118fd0178a3dac53053f413bf4b733e3b 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 3dd5714eadba579c62e347718a073db83889a13a..07cd5ea7a03811c1c01204ac9eb6b42458bb2a6f 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 45f9523f968f903d463d56f5baf927662b7ffbc0..cea175d1989076e8f53cc3550efefe7a48dabf88 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 7b6dba3ce55e2d0c3503fb1141c9d70d1b1f52a2..458772739423234388c50940bccbae9c3bb63bb6 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 56d470ad5a823ae9825d7d36dcff383a6dfb87a4..23e872f8474286b205d6324a70b7a3637618e50c 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 20b5fa92c53e7374977b55800ec95c831239966c..b1425830a24ea37fb0a081fcf95788e7891edfca 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 f36cab12bdc71964a463a79e4553738f00126dca..e66154e5c1d7710e0ddcc8c6183f707be0e388ea 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 e79749cfec814b75f0ac84d2f5ad881399a79a75..8ad3a57cf64032446fb50fc28d2cfce6739bb18b 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 d281f77d5c2898e7d5da365afea9ca5e7bba8a7e..2c54586ac07f05d70b9afb95f993e7810ce5abce 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 9d08350fe4b035cca635d3368d75591897e4e035..69e62f42e2e1388c7d99ad15bffcd95c234d5d40 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 ad7ad857ab2a9effdfc9a087c8c03ec3076db976..9aba044dabedbb1fe0610560854e3b7ecd28ac44 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 5bcb9a71ca80ea6bca28ec1f3b12a47aa13eb13e..b2fe6bb3a38b0c425d2d31b167351382f1cae2dd 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 eff9c5fde50ad65662948cb9109341a0c25a7e0f..91b6b2e9b79228b6c497f7c7aba65dd36415f9da 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 d18be19c96cd3892ea070bf4b48bb32cde4aa817..842654d333177ad197326b8655b80b54bbdeae23 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 9f462df77b4aee13ba756fa97500e435ef801bb5..e03f1776f4f4f416c2bd5a1d0ff63f503e976837 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 6f738d8e3a8f3ef708451ba99df2574c1ef488fe..d45e0e3a781de88a72425379a1390109aa795968 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 aeb56c53e39f563dea2768c005f2df33ad90df6c..d4e15a617c3b1b3b62128ba5e6ed236620bb40b6 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 b76ec0e7e685ee8b1fa4d3fd99180a96b595e8d0..6fec9384d86e4877d5b750a9f9a9ceec53e06bde 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 1d384a371b41d6c82e335c617ba1a683992ed8c1..5b915936c3f3f78d79f6fbabd9feb195da4fc00e 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 e968c3fdbd9e8e63b16982ba0111401c3b93ad4a..bc74224503e7d0edbe0a704f6ab6a40ae959bd80 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 d9f65d7e3e58b64dea5b930d5800e20921fd8778..04139eec8c4eb9ba5780d360b37bc5dbc8b204d2 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 91f3dd4cda1b66926ac9ac77e50e17ee30579160..83d85df4853a226a9ef105f529628214a75426d6 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 24a08fa7e32875fe223ed3c78aef8f9d5f984376..912e1cccdd1c608b57c16b7af4d91833935e9172 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 447afbd904a4bd9dc3ca1f75169083798b879e61..8b5e0b3a92a0c4a408af8bc50217279065b78803 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 66eb0baab0e9948cb4beeb88a1bac60e71c19821..d0c281f41a0aa98f368ec37fed1748e31284cb62 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 c6532de0eac79652b1bbf6fefb8428cdced46fae..a3b1ee9c00d7152405e9ea0e71549f350194692e 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 004d8ace5019a7921e1ff7e0cc189e4c260beffa..c1f8cc6f14b21ed74b024dd8c0b4425ed1425cdc 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 5104c802f72fd7d7175dc763fb72091d60b35b92..d1dcb1d2e087f43fe81a8d0f966e6f65644e7b4c 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 7344849183a77bc51e3a072dc31875dcba8fee90..16fa7ea4d4aa7d472ff9f59f6b0131ead21c2902 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 6a71a965e757fe50870bb45bdf2c25713a972d81..79f20c8c842ed1a1f12b34a0eeab0e133fdd3e7b 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,
@@ -686,6 +729,10 @@ struct cx23885_subid cx23885_subids[] = {
                .subvendor = 0x107d,
                .subdevice = 0x6681,
                .card      = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+       }, {
+               .subvendor = 0x107d,
+               .subdevice = 0x6f21,
+               .card      = CX23885_BOARD_LEADTEK_WINFAST_PXPVR2200,
        }, {
                .subvendor = 0x107d,
                .subdevice = 0x6f39,
@@ -698,6 +745,14 @@ struct cx23885_subid cx23885_subids[] = {
                .subvendor = 0x6920,
                .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,
@@ -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 9f63d93239ec9d279f1de77952e6459ee40389fc..edcd79db1e4ebc5d8ba6319b2ccb3c900a3024b4 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 971e4ff1b87f2f159aac208884b7fc1746ad1fa8..05492053b473033da6da030fb1f64d7dcf2ea0ef 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 7875dfbe09ffae666e8365e877022af4d16a7be2..8a49e7c9eddd76f508f8131a8533f552ad7db98b 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 161686832b2046c173756953485cda329c77b2c6..7891f34157d18806ad400051b95cd2abcdd14023 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 038caf53908b5acd4160b3e5167ebd4f27a69ea0..0fa4048ab872302496925e0ce129acbdb53bc156 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 3b409feb03d80a4ff178effffd238bc0b03ce466..f2ebc989b30338b943e1406780a61a219db7c7fc 100644 (file)
@@ -45,5 +45,3 @@ struct cx25821_board cx25821_boards[] = {
        },
 
 };
-
-const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards);
index 22fa04415ccc34f0a9ece611b72f68d5a5996b5c..43bdfa4dfba11c54ecfdffc98a0ce07c77b070b7 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 6175e09618557ac5bbba50d9413b1e370db4d8ee..8bf602ff27b1049e9bee6f0495ce205a4d753822 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 88ffef410c503f9d750bc09d5b7fcd2b8845bdaa..1f43be0b04c8e91dc5012275d3d040eb0bd92a2d 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 aba5b1c649e693d3b205e12c2247f2c0f8a3b730..400eb1c42d3f9529a946017ec3f6dd4fcae3424c 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 2d3507eb48972046a5baa70772fe685df64bfbec..74b7b8614c23af893b95807246b180f151bedda3 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 ecf21d9f1f34bb1e587edbdc6283a177ceee595b..ed8cb9037b6f30f0876ea377c42db799a8dd0e51 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 36e34522b9a80878b61e78ed10a5276f3e7b570f..9375f30d9a81bfcc5fba2a35953855f8741a6610 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 ab797fe466d2dcd6d389f3ace68e77ec17021bd5..e60ac35fc10c734dd8b31f7901a23347653dcc99 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 c08ae3eb9554d46fc29b4dfbfbf0f314c8b90cec..802642d266438ffaaf6f136a30e141ffb452810e 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 a846036ea0227dfaabcc64d7e20401b495dcc424..9e89e045213a52e57e8a4b389ad0f9bc2dacfd5c 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 2381b05432e676da50ed335ceef75bca5f5e79cb..54d5c821007cb4280ff5a41e5fbda3bfd2f5484c 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 37ebc42392adb45f0de0be794548a17761be0543..970e83308525b3a0ec74cbcb5d694516d3d2d444 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 493828500055297328376740a4e2a58d40a57a16..8164d74b46a4590af0db79f56bd8c41bacead624 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 75ce14229e03b6e52c70bcbd3f67cc44bd3e8be7..db887b0c37b14ff4167f2e8810ada690c6a422d6 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 dbcdfbf8aed0bdeae1f5d6ffff71bbe7a4e7d9da..dd67c8a400cc5d02f20b33967d27c9698846355b 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 45f0aca597ae4c4938feaa6b18cd38d6d0abe8ca..27d7ee709c5895ddd29eb956992c6a9ebc97bff9 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 d37ee37aaefe5e4b681f402842cc33c49ddd8cb6..57ef5456f1e87e9cc042ec8dc05cdb07192bc15d 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 f1cbfe5269895186372ed4ac6b30c64e728e94de..6299d5dadb822758bc4a2d9c1bb8a922bdfcd193 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 26ca8702e33f3449d1b03bd441fb1b139804b753..39ec35bd21a517f3b362c795abce5a82db27afa8 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 923d59a321f8a9e150685447ecc12fc54044ca24..cec5b7553f284d40c0cea10a7ca3d4fb6b24cdb9 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 eb70dda8cbf362c83ab53503eec1948ea5f36004..d7f0249e405004139ff1b0e42578170682ef443f 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 4e4da482c522a0c2617301ab8a135fe488917ca0..1348ba1faf92ad33d047d8244e2590c77cd99e2c 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 4c11059770903621296ccb286a650a10a9b027fe..281916591437fa8196cf9312a873c36135a7f915 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 4993610051eeeb586005712060732ef2539390b2..bd72fb97fea5ab05924c4eeb4f5452534f3c3f46 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 04609cc6eba70a3a5ad40c04a01a4308bccb8403..eac472b5ae83baf0cdefb0fb2920a1e99b925094 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 93609091cb237d837b513ed8661a40cbce0e17bc..d762246eabf5a3b78a8c348e7a5e5f149580e143 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 1089834a4efe8fec9845b13a27a06f1f7564c720..52ac5e6c86254d0bd7ba5a4dadee4ba6ca70d08e 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 76435d3bf62d8a735c277c66724995ca78b8ab03..ef0a6564cef910c608684f1b9294c53212a36637 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 e576ff2de3de033cfc888d2afea783d72dfd93bb..810c3e13970caec5787cfda77ff5c3c30314997d 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 d2e6cba3566da5a1462c648c05d6d796315cc272..f3c6136aa5b4767af56e4de4078af6cddfa48735 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 a8351127831726bea95214d5e55d7f75535ac6d2..7a4ee4c0449deea95dc86e82a85af8412a1d5aad 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 540516ca872c53a05d3bce4dbcf3cb9b3a87b4e3..65cab70fefcb067e3de0a404d47bd41feaafd38b 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 5184887b155c7098415b1a3470619be336023f87..32fab30a910590ba290ef987aaad8ed1df78e74f 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 b5a19af5c587103c1c134b8b5fd0e19a13dc8853..3458fa0e2fd537916270fd9fbb0908d2b1610821 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 6a17676f9d7227063fd4d22d9650d752e2695ba1..8df5975b700a01c8f433fb902e1d10e2983de5aa 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 fd6289d60cde2c8a25450f6cb6d24c18923f0e81..0b2948376aee9d3715c67d97346d6e775d48ab48 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 084263dd126f08bd235a46a7a54e3d855516050e..5f2c4ad6c2cb3427835ed2f40ec9ba5432ae3221 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 ad4f1df0a18efae5c198888c5198f3de7994c205..9a6efd6c13292dec6ab9ba3ba3c052ea9772f24c 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 db796c8e78747347b405debefa01f9bac17b8489..ec1a5947ed7d00649dcdaf3a7e8b151695fd6902 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 41f5a3c10dbdd1cc9a7b6a1a4441f80e8109379d..4ff3b6cd684274a33f14bbce053657db4237f220 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 368582b091bfb35ac65ef7bf81705156fe36ae82..58ec7bb26ebc715f11b145f72149b9a4535a9297 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 b93a21f5aa131656cbc62649e164031ca3a08c86..74344c764daad5ebe4fa19011a83d70f5f8751e1 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 3d13a636877be73ce50079a3d286014b6182b3c3..c9388c45ad757b48d42c7b45f7b603d03fdd0b12 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 d02a7e0b773f4c9acec74459ce4f8fe184f3674e..6866bb4fbebc0d553fc27fdb3f72e8d2f6791f9c 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 8df22f7791753c9492665c985a639c6c53dda8dd..150bd4df413c321ca5f48a5514213ed1c1c306a4 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 387a232d95a4b63d74a9e3d2ef8030d8cdfee08c..4b8c024fc487d3267bd905a0b42533b26e5975f3 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 b557caf5b1a4791846361ebf8ac382c429025f18..ccdadd623a3aae3ffabd49be553709d5a9511ec6 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 21db23b196bed15eb46af3d1214fe5774a74062e..fa3964022b96d939a3c222b90d81bd5bb2822255 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 f1e3714b5f16cdf017a4868ab5eb31ecb5899515..93d864eb830627898c82d3d33416466819ee5c76 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 b91477212413ac0f46e78165cbb7b5f60b64af8e..3db8a8cfe1a87f4eb80e9c22950bbcac93b7daed 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 9fb669721e66d8a3ee48850c1f77f8ad34074873..d86d90dab8bf880666a05ca0463aa83fc62f77de 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 97c2c18803efd7b6171f0b973396931f7c6ab7f7..9cf6731fb816b4fe2bb27c6ce06f37658995faf3 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 5c57e5b0f94920b8ed56fcd6431004463d8194f3..0e750aef656a6f8a053aafbd965b4e9e1fe64f23 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 e5fc9acd0c4f5debdc03cb4645ac2cd3f2a23c6b..2a497c80c77f2e1c2996403c356992a771c5df82 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 fe160882ee1061760edc95dd79a5c09455837bc3..9ec48ccbcf0b2acab93f4eddd6eb31ed4998de34 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 06ac69245ca1cde81ddeb5018fae23f84d84e397..69e3245a58a0cbfcc1d333d390a30aa45e4c70d8 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 253f307f0b379bc5d57271e630343c0e0b35c8c5..4b2e9e8298e1f6e962a468eeb1fba4b91b5c68bd 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 11e84bcc23a169be3db8f8a3168a458bee0a31d0..904f11367c2992fc502c4632c5d4a8617cbde839 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 56bacf07b3618f37db4cbe5ea39735ec9d6065b6..f4eb32c0a455e04058bdf52fb2158d1dbe7ac479 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 82516a1d39b0777d7e591ef89f850b1cce1f1310..b698f3d2ced938582a80aa9c8075add338921fb9 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 07aacfa5903dca6430bda96cbedd733b23d5a300..80c611c2e8c28fabd83b0383d19670cf1cbe0faf 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 19632b1c21908cb52e0c483f7a7339f647756e1e..fdae05c4f3775d7f54050c8776f0b882ac2fe630 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 31b955bf7664497ff967ab08e55271d99ca70eba..b1e19a26208d88627ab95743717e1fd6a039dc54 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 4d13a7f2e5c3bcb64f65dff524d233f85af0d506..492a05ade7e190b3d3c874f7b2942b3138a796cd 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 ba81d9697cfcaa3b653ee6748df368ec5b6a094e..454ea596a7eef89fdabbcf1dccfef4dfbd0f9ce8 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 7c3674ff5ea2a06073820bb1b19d2b7ec0097ef9..07e83108df0f6834681887c1902c6742212324ed 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 98bd4960c75ec1729ce1930c90e8e20d2db44bef..904baf4eec28ac902731d90bb78ae6f73632c4cc 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 6c96e4898777f29f5c9ae4457d488e9424b32f2b..72971a8d3c37978ef1c42b6a2523bb4cb8e92f4a 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 f4d0e797a6cc456229a75e74edb99a9e814f1526..d74e920568104dad4070ad120f4ca1953939d83d 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 bd8f0f1e8f3b5432d7b98939993d3ac8e0a859a6..b4162315773d90d314e7476aeccfe2d3a396c140 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 81f38aae9c66f6ba2893eb1ff5ea7f6c092be96d..3aecaf4650942429eba75ee89cc33180aac4ae07 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 1c23666468cf69223542b5f89f024a1201c4733d..d9ee43fae62dee4f7f5cf474d4f74ed21e4c5a01 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 e4a84ee231cf458a4e5b84292f2e83115e023caf..abe256e1f84324d36041fc67af9ca4587f113cb2 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 2d31aeb6b088997fbed7678f631a47f175500d55..9300e9361e3bce840aa8f517b18a1c5cafd32628 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 300005c535ba076ba0e90bf0e7e58b1f25f223ab..9823248d743f8de71da853cca6c3dd215da7bb1e 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 878d2c4d9e8ef545d7f76bbda04f5d96ecf7fdbd..4be5cf808a40584d949e89fa78e7a90c086a31f5 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 8b6275f859088d703fcc2893412dfe5c4ccd58b8..0bd96906339227ec8b3df4e78640fc500be4fb87 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 be17192836099c4e1759e2e7a9d41cf7e9cf3a33..351a78a84c3d618346167563c15d77a204fffeae 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 a384f80f595eb7805d0a112b547bd18b983c0bfd..e9d017bea377069da087751404e75f86b081c7cd 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 d7308ab7a90feba03c8164bb17c5bb56ba2f42bd..2a34ceee48020b886ba2d1ebd832dd2e3ee4ef6a 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 d556042cf312c280ede9bc1851efc1effda5c042..da47d2392f2a204f0832608821a743baa2627f19 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 1ea17dc2a76ed51b6560ae642c6f31301f4a57fe..c8fcd78425bd228ca4374daf94fdbf4ed1c0c17a 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 e97964ef7f56a847081dc99efb278579090e6d2c..2627553f7de1f90c262f2d234d1d1faa9288f45f 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 c0cd0848631b22953bd09723aa722decedef8687..ecca03667f9870d865f212bd0a59932aa9d7f00d 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;
@@ -919,6 +937,27 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
                fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap,
                                &rtl2832u_r820t_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;
+       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;
@@ -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 729b3540c2f9b4fe28ffb97f59daccd910e3ea6d..2142bcb41b414b42a45774babef70529c3658d97 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 ea2d5ee86576565930d0336605a6e20f6e0528b8..c11138ebf6fb964ee800613d768ef6639e15663a 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 3940bb0f9ef62dd8b3121d3ee0a7739669581d90..20e345d9fe8f3aca89a913f79f3ccb813b7d5ab4 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 c2dded92f1d3799a331f3576b0df617afac6d4db..6d68af0c49c83ecab8d542eb0a3942902e70e7e8 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 6e237b6dd0a8d8acb5bba115152e5b060775e680..c1a63b2a6baa53725342b4ef0b6bc93234207275 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 73cc50afa5e168fd4e63664f68258ae115c95f10..d666741797d4e690fbd6a62aa2141cd50ec1e507 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 dc65742c4bbcabfddf54f8fdd9797d6bdf162a6f..a5196697627f3be4098d849111731b352f989e0f 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 bb1e8dca80cdfc88dff7919b70da410aa617d1c0..344042bb845cbee2326e47ca021085e0299447e4 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 9d103344f34ab5e4e3267ca8fa08f58db1f5a741..fc5d60efd4abe99f19acbafff7b83879da5ff3ac 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 205e9038b1c0d406989a180d059b9f68e7b517c5..f8726ad5d0a8776063c6acbc482c9180ee10b08c 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 38714df31ac49878d13d64a9e626f26f1813041b..2e15c80d6e3d11b953d210b553cbc6878110427f 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 064b53043b153359d70b2b6f3abdbc31a9131d6f..f23df4a9d8c56e460840082bfd5bb9ebcafce511 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 048507b27bb2388573ea8d76077e32a5eb1c708e..f3a7ace0fac9cc8bd4e2b2c8b82038dc85aa7068 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 ac0b11f46f5037d0cb29eaeb8896b445537fa76b..300642dc1a177207c1e4b9952f20404e19667c8e 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 8da3dde383853d9c0faaad80a7f0c53eadaf449b..19736e237b37d6a14e50141c8d24104141783f16 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 fdaeeb14453fbb42d82d9d31c0d64e088333bed2..5b481fa430992a7b37b9d14f9df108498f0c4d86 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 cfa4663f8934ebea47eb7d8d116a4405e875c17d..27fcef11aef42998b0132b26f3b3b4790ee06dd7 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 ff2c5abf115ba635d59a363f190d04acb21ceb66..779a8785f421bbcb67c62840c8a67478ee0a44a2 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 68bb2f35966656e8005bb9ef6cb08707ef266bbb..f006e29ca0197b66ec62dfd821dcb520e24635bf 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 44c9964b1b3e206085ac1ea3dea8e23048aaead2..599f755e75b86513c28228468bc19e15278a414a 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 8937d79fd1762bcea5ebd863d2eecf6d0d69c0cc..c95f32a0c02b4283d0f4c66b253e0a7e402f392d 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 03a33c46ca2c0ede859867d00b0652f911566be9..90f0d637cd9d05f66450ef645859cf7f5f7fb381 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 c4cd028fe0b4c8ea0c662ab82fbf98695f23a722..47085cf2d72365b5fdac410d5394bbdb8a7407b0 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 83519be94e58c704b7b50c50b69cce558645cb1b..cd79c180f67b87e84689a8a162b4962bf5691cde 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 1a5bdc853a80dbc2fb9a3c15360f2243a7f77f91..25f86b1e74a80b9c9d6f4d856b8156a216d47102 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 5f729b8aa2bd64bee74af9c02b1954a12c4bb304..5102cea504710c5c2aeeba1b0101159f35b0b459 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 f4453d52801b94140cc1201f437ba68fec694095..2a38621cf7188d1f8cdfe03e7d1fede7ffcad566 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 d7ff3b9687c57cb22c504e7a3a9493e41fd924eb..7277dbd2afcdb8c88629aafc2c9013b38cd79390 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 3b5ccb1c4cdf11e0597995f1acdd3e74d182cabe..c69b45d7cfbf3995281ce1ede849a6b761474d77 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 688592b289eafad0055e4f10d747492a6b65d0b5..f38fd8949609fd917cee8568a4cba9bdb5e83cb4 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 9f8bf51fd64b64cb1e4544a722f2b0d9da308c1e..f011a309dd65f4d0776e82cb55f4e6c3fb4ccf58 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 acb19fb9a3df3a7d8914eb4f5efa927aa6a18fad..aa21edc9502d67466f7ed13959d85709b9e36889 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 b10d0821111cba7815be078d338c8e1f0917e5ef..e274cf19a3ea22a1448f340f7a779c5a1274d528 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 8c0982607f25c0ca8b14518be8ebee8b40b50278..b0c70fea760ba68a8235372e9c0ddcc2970af941 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 585868835aceab032418a13448cf2d00007947f8..1fc80af2a18907e57d54eac3f58481ada0f3cda7 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 55ee7a61c67fb635fe8dbfb2527f178c554c0f6c..49d209bbf9ee08f54e2caa86283205b914a72afd 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 8206b77433006690f3926767290a2802de816611..8d785edcccf2ef040906840f55ea3173e555ecbf 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 af8767a9bd4ccc1a9af62d7f23c69e68a6348f2a..a517d185febed4590bbb5f58306aa4e4a2c4f0f1 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 4cb511ccc5f6ecbefadee9ef64de47f9fd75f3e2..640c2fe760b3c4e13c687eefbde34e156025d648 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 8591324a53e15edc5582d73d9c90715969ffe0f4..d497ba38af0da06149ebb95ec91526c21e6a4604 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 a2275cfe0b814625b1bfc9e14144084aed74ca6b..103f6c4236b0789d9eb621eb17efd77a5d46c63e 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 2165da0c7ce1570828be04c572a0bcc816353677..fb9fe2ef3a6f60059b7f3fee4e89254b4869d52f 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 7eaf64eb867cf5ebdef1e55c265cd6f7ca4a6685..a41aa7817c54349e196a4aeee80c60a8adb4a921 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 cbfc2f921427cd250556210bdbcf37539c6d5e88..7b95d8e88a20240305a8817b72c3459d80a5adc6 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 6e5070774dc2ca480fe3970c75bb79c0a893e47b..2f0c89cbac763bfdb5d8434bc19768df8b8f977b 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 c4d51d78f837771eafd6fb170cfc4f4feb7a9734..ea05f678b5597ee801c249fd3a22694df6f34edc 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 03761c6f472f89d920823384930e8b71a23d56be..05bd91a60c09fe91d535aad8323cf0a49b48fe16 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 95f94e5aa66d2a14910bc0b81eec3bf595534f02..3316caa4733b6a48951f14f8e3e483188e325eba 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 e52c3b97f304d9b2dfcb1e32bb9a24a3c08f45c6..29724af9b9ab11fbb36c1871bacada0fde62409c 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 a2f4501c23ca3502d57be595afb6f6e91b84c903..0eb82106d2ff4015bc8cee2e0159626b8ba4ad8e 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 3394c34320117542ccab9adc4607a1caedb18f53..899cb6d1c4a4a74a68cae01dbea51de5befc821e 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 ddc9379eb2769c308198cb17bc9afde4e4067fb3..20c09229a08ed7063cc05a02a30909c3c7fe7bd6 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 c85d69da35bdee1b2c0f82ac9deb395e8450b786..85a6a34128a8ecfcba63c372886bcc00cadf74d7 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 b67de8642b5ab3a675ef8e7ed62f35a361045f89..e18cc0469cf8271fcaccae98a4cb676cfa12c200 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 037d7a55aa8c6c0595987d9142630a6ea2c7f5b7..433d6d77942eeeab78fa7282fdb77ccb3648a0e8 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 c3f0803886843d3b3c0ac8abd8474cf23dddd301..60dcc0f3b32e7b445352b455d377f5b2b794b4fb 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 7c43712882158787e951ba9a923fa86d1eb67f90..73035ee0f4def8c7425872dba6c1687419b1303f 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 de0e87f0b2c3a833f113121709b9a496d29ef1f3..b19b306c8f7f533d3112db441f692e57bd76638d 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 16ae3dcc7e294cdb4722d0991b31b9e6ac7cacf0..2f860543912cd1c3f5dd4f286705b2c8b208dc37 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 bbf4aea1627d389339526a5451b3ac1fc7e4815a..a0547dbf980645104d862fdc39e2ad740107ad15 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 9188ef5d677ede05a5cb2c56d176bfb878321185..24f2f8473deeccf796b8e34771c1f37085b6b4ad 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 96e637550988585fec8d45793eefa159ad9d770e..c75198dbf1399eb1a9e37b908d3d5d71d874200a 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 1b6e91345222f7aa3e41bb4c7066fad833feae94..31727bf285d0de164d00024a5c34fe539403c94d 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 7ebe9ef1eba663006399ec1b52f327e3fd3d0e47..c9b1f6422941eeaf628a0f7a65fd4e65053a8a14 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 914c3d142f789f0a517d32e59f53b457b61c2089..62a60caa5d1fe7eb583cb208fef9b96a5b0dd8f0 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 15b905c6553c07e7953b7cc4b57c63fcd5ad06a1..8a28dc90fe785033452a1740973f34883abbe0de 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 6f68472e0ca633e9b0ec5f08db4f1389af93336e..14d9542a4eed8d1408c556c781795e0c24869a9c 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 022b1863d36cdf5af689be720a972baef3a54820..75e180ceecf3fac16f9a432ada482130624dfe01 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 51dbabf7c0217f185323869ae1259f75aa7925a4..beccb790c9bab93869015044142666dc786b9508 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 47be7b35b5c5b09c9e17c4ef0cb7dae32b0c2855..1ca554b18bef76c8dd92156a2c9bf7ed87127fc3 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 abd3ab7c0908ab6232f0e7d82a35361ca33c46c7..ec684fcedb42c5cd5be4178151d0ee5c7249a6b9 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 6a9fec40d018308a39e1ea24b086bfc94dfb77f0..c319c4ef5d499c9f59140adf941987ac6d024bb2 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 7245b0c5b794be489d1c5d250c196c92e028a48f..2ed774e7d342c64211bf862fdcbdf0cac3e57170 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 9483bc8472a51acbbc45a3f08b6072104a81e027..da1c6566d93d2f755878d358a018835bd7cd1454 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 8cc6aac27cb2d5ef77b0c7894dc9670afcfae4a8..fbfbf0b7f97ac1cfad5d8964a7436fa05122a89f 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 522be67b2e682d743c36534005fb09737cfcf590..34520cbe8afbd6e9f0daca77f4bbde41b93697a6 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 1029d018c73921828f34740b4034c0cd7df5c3bd..66b58fe770944d714c69b57107924b17c8ff4fff 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 c04723efc70709d4dd81db288be1fa11fc6deccb..9f92463f4f7ecaca1b005a14e6cd59f7d38deb4a 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 e9b1c93a3ade36ba41de62bff0bd588fd4148162..3bbfedc07f41c8e24d866120b64423374c629fa3 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 de7fb80a60528e8f3bcbc2c45e934dcfa5a65181..176aa26fc787eae4ba7196edcfae535d22ae50be 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 cee098c0dae36ef4ef9baf9d3fb07ec11e24913f..791aea3e96ce27622032a0c0fd1bc52ecd69203e 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 f745e27ee874a469e734cebd2757542388a0d10d..898bd335cd8ee99d7d8a2c233ef455801941ff30 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 adc8ea36e7c483e5f9ee05e1587d3fd1b6029719..2676492447378e989b019d05e223b622afb2c79c 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 29ee54d68512e3ce38dad4b5d6897f192b8295f8..142650fdc0582e1d25f99480e4a2fb13e1e9bd23 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 e59ac4cbac96136c7cf34607030a408428c6b3a9..0d946ae14453380e4abca019a09519627309c5b6 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 135afabe4ae2ae850a6897ddad06dc1969dba301..d280d789e55a5cc0a60896eee95ad8de3ee1dac8 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 a6841f77aa5e709d472b120336e25e5572646cf8..484fe66e6c884e7f32c9c9caca6473bcfc5b81e2 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 3b835f593e35294a4666c25872eb911e9726b131..573de7bfcced0be23098434759a16505291f0e75 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 e6ae7720f9e15bd546362509bca185c7a64eb86f..11e20afbdcacb7d31e49798db6d2dfbf0504afd8 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 f530e4b73f19abd63fe42ba7f18230dba79c088f..34c18fb8c0896b46f49de48b4d5631a5d780a811 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 33f040c558d090aca5887356bdd1cd2bbb11f461..c2c8c91c6c7b3188180301e85c6a4ce5ffff0c77 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 70f4909fee13b8d30a632029a9639b41b81cb051..87ea51dc6234a243021bce1682a213856c49128b 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 1c2b994e1f6c0b0ce1e4bd346a4ff6e1e1c77e3d..71e3e0c5bf730c7e8f3423619d9a2e2acc6fa27d 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 baaf5a8123bb8eba1aadffda901238acce2a9a98..88718abfb9ba0169090201e8fc04037ea4f32854 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 a6755ec7bd6ac55a1a8675a2d0382330f6863f54..dbb34f94e5e3fce2e0df7f18097930558069af2f 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 5ad4b772b09797bec97c9759bb9c80e609102651..a081b925d10b2debf94e28ea2c5c500081262309 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 b8f48647661e7db4a58612208bb51ed8957d3fab..b7be0b29557599ab94c30ba4f16534c88eff030b 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 f54fe4d4f77b34a7ecbcef449635dbdbccf036d8..ee61fd7c198da714c2dd56ba7be2bfe6da5a068d 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 d792772048358dbc9857cd3090d693a951d48713..c0f608e3ca9e5fc1fc0706666b8fcc4f89778567 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 daf66942071c9084779497f6b40ac7bc2c8c916d..0779d5ab9ab1538631981aff4994743a5b96b008 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 d5966e6b5a7d8058a9ff37f15a86204ffe6ffe6c..0313f839e8fadc32228b9c892edb31a449b53ace 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 802dd3cb18cf06a2504941e0c21e5fd37ec9e7b4..1e9a4b2102f9d89c9549e3d226cf7ef0c91866ef 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 3113e39b318e27a7aa2e74b7433e601edae2da72..bf8b3b5ad1fe65d03eb067881599c5cbc1418a32 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 e1c283e6d4e54eca7e88e1404ed9d1ec72497cf9..0308275116672c2b15ab2d91b334c561dadbcf46 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 e760715bd9cbd30c01c6c16da430c06931fb04c1..a3e291d0df9a19211c7b3d855fc31ad3463524ff 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 0b7ea3ea8bb86cc3971c7af15258c285100bd8e1..f45473e68bf71d7ab65e40e06c9bf0a0484242e6 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 08b18f3f5264aa8052cea030143d5bc5c4b76cd0..9e2b985293fc08cf30bef9a9455fcade1eb095cb 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 94b8a33243192d4217c54442424bc91e1dbd936c..d87f77f790d67fb49019cf94c69e346f50c89b28 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 914cc9b2caad76b1916524929921a87c270c47a5..8aa42e738acc6dde99a716535f8b27efcd9d0988 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 b079c65eed6d2f77ca8fbd989180c3295c0b269d..7558d91864380ae849d90a24deee4e31985850eb 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 83907c72059420973fbccc2512a8db8c553e6300..96853a09788a9a76b15da513007c359c0fd42e0b 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 1a3163f1407e2093a43a6a1c61db5a9538f6a329..29d5d988a51cc6233ce71215a3d88bcc91167c94 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 3e227bd91e81935eaf3f4ffce830de99b3e1321d..64145a32b917b37ce862e392fe3600417c99c336 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 bf18b6bfce487b2dd6a77917344c1514dc1aa7a8..57a2b403bf8e9107204c3cda03671a5a1c8bd87d 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);
@@ -917,31 +918,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 5345d156493efcc00dcb11790aa145d0f4ea51a0..443a584660f0132de2b07ee232e068575a63faf4 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 6d02012a1d0bb72201baeb0b33c56c8ff3262938..f631f5a9bf7948b848754cc3ba475619c957a79b 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 ef183483d5b67934440cd8dc1a8d6910467e6a61..e5b5eeb548d17eb08e1f374a83024dc5e4a1170b 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 5e8823dc3ef613b70e24ff3d6e16c143da2976a2..6f42050b7ccc6d829d6b58ece04b1177e73cbe80 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 80d89cff7306533071c8f7f71aac97205b942d31..4d721c6e2af01026ea967dbd54bdaf17c8648aee 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 ef8956568c3a2978b90cab02a9dc0ed56c76254f..157b570ba343e4648b796e7330e7b75bc052d00a 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 69e438ee043e6f8a9c151d3447f6a9e418544231..2cbb4516d3530fd4167db6533c6da4740ed60a46 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 df9becdd2e991739010fc62638757bf02387aeda..f5443a6c4915d2580ad8b05b38a111318e3b665e 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 94fae2f1baaf4de39f9c0e929f35c973b8d4b0b5..2b7f37e82ca94edf7c875c8cd32614675d625a54 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 9d6e2b844404d681bf5b5e61c22ccb026cf51181..1087b4c79cd661bbf8bfe57f73470e681324b668 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 e9fa87df909c248d4fc6d9c30244360bf28327b7..d6153740b77fa174060679a4fe3d31d18e619eb9 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 6a1fa2110a057b42d2a79cfb3407bb0f8fb8f3b8..3423c5ed50c7a0ec918af7c7adcba7b2e5d2baed 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 20897529ea5e10185a3d39864755ccdbeb8fea6b..5c496565529796f06ca4d26b280724376aa6348c 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 14b5961a851c43ed3a78e0df34d7be79e3cc0162..3e8e53ae3302b7231c64acaf7de991b6e6861b35 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 018f365e5ae46c71a6e8a0f2327e9fb8775c900d..4bce0deec362c02ab8b5c99cf074ab0b7d669d6c 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 81b29941c5b9b1ddaff3f313cab7bab9c57c6e46..6bf24ab917e6453a22d09689befd58e40fa96efd 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)                    \
@@ -183,6 +190,52 @@ extern int dw_mci_suspend(struct dw_mci *host);
 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).
@@ -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 66516339e3a0fe474533f62fae475283180d701b..de2139cf344477bbe8922448a963c9e1094dd882 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 d135c76c4855b825175370e215979bd528e4b2d4..f32057972dd77fe9a7487f74aa2ab727c2126db6 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 b900de4e7e942c38c6bd5c78c521325619b83aca..9405ecdaf6cf952f4a5d2cbd0c75f0d745915ae4 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 deecee08c2881d0ee6ad34a9d049368e5d05ceec..45aa2206741db8da3bebac2f6b94385add678ced 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 c174c6a0d224f86bcc2d91dc524940a5e90d616e..f7199c83f5cf8e85c49ed54b575502d0187fc8c3 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 e1fa3ef735e097e1c3c8e533fedec63d30786f43..50fc9df791b2934deb5acb25532440dd32347551 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 b94f38ec2a838acff3f67861a8059eb22d41c2ff..0b10a9030f4e2a85029ea4bab3afcdb8102899be 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 6ac63df645c405b0bd6586c7f8d993d940f8c1af..dbd32ad3b749337a196c98915694b0c013685186 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 1956a3df7cf3f0e2f03dd36775f478b454272fb6..32fe11323f39e10bee30cac93daa0ba94e771939 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 375a880e0c5fb899efa5a2de9b77543fcf66d12d..c46feda07d56883b1b6e50cbb46338d73c82bcca 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 8d6794cdf899cd812e394b862c111a6c65b53dc4..2fce5ea5eb39cfe7f72152f75a0a852325640059 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 85472d3fd37f76b7a4b24b4a6f42033ff9dec472..7a190fe4dff1b799958bfedf7bb01dd5d405a0f8 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 36fa2df0466007f7b618aa9986ca9aa098199b32..f6d8d67c545f882678ea40bca4f2525a77bf28bf 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 abc8cf01e6e3317ecc3e95d32aa66236def768e9..461a4c3f4ef725924319058e531aaa457bffd7d5 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 a2a06420e4635b3904c0cd73f662b21623994de7..a7d9f95a7b03dd81af540212ef8016157a595c5e 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 e328252ebf2a7f684d0756dbfe1c1b20935d05ab..0b249970b1197fcc92af6e63184c72cf7c406286 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 d7d6bc8968d2422d8b2e9fb44a695b35b17927d4..8f753811fc7a6ccec6ca8bf5090cc98287013f45 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 7a7fb4f0d5a43a4829fa1e258afbf112f4dd9b82..bd8a0982aec33ec99b95b97fbcbdd65dee642770 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 b037f188fe44b26c357b77ebe985f8406aa4c1ac..0a3ed01887db824da03a66f103ef4b4948fc074f 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 50adbd155f355bc00676fcc8d986b889f3c28040..b7e305775314fa75ccc58dd67752420bda8494ce 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 36629a024aa1350e6278237f42a6d92e97b33807..d032b080ac4de66311e390458629204332a6ca57 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 43d962829f8ec60326a0c63f3b3274871f2904fd..d1760ebcac0359e6e25273ac849f4c89f60f6b2b 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 b3802256f954b24d7da9f2b3dae35eed4091cae1..f3b2d8ca1eca5cc831d2269e4617938bf3d90bfb 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 4f84586c6e9eeed9cafd5f89cc83c530b46fff87..63fac78b3d46aaa0a76861cc8c359c441b367a76 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 e9028ad05ffbe3fe070e91ff9f7dda4a6a71bdd3..4262296c12faa7bb210068d3b62be7216040fc5a 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 e954b77588769ce6950ff73f9c8e3c5c3320c531..1defd5ed323668780846c6fe85041dec6205c20a 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 34231d5168fcf12dfe804d2a26b3cda2c6647976..e902ed7846b0544012a289368b187d541ced1969 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 d78a97d4153a98234998080b40c4cb94f50a0876..59f08c44abdbc9be920ea62974d19bcdc7884889 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 bd1cb672034fe770d2db2b986caa1821c1f63f58..1b0265e85a066b2b8903d84645d1df918e4a9ee5 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 3dc1a7564d8725d62085b16cb7c0544e138858b2..8b2752263db9a5549742bb36c3dcee48999b8b62 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 4edea7f4462f831c52abfa7a167e602f362902fd..9dfdb06c508b05439cd93a71ce5c30ef8558335e 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 9dcf02d22aa8fed1a8e4006952f9d7ee27739c9e..325930db3f0481b28cec6bbeda63fc2e03052d78 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 2362909d20c00f740c086049d410aa2450f81d3a..6547c84afc3a4bd6cde72302ddb1add10141b154 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 bc8fd362a5aa7624fc01a6c79722f4b57e824aef..0ec2a7e8c8a9588170c97715856e7b5f34cf7c60 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 77a07a12e77fea8e44376e40fd4c0d12caba53af..ca31286aa028158341a5847382b75afb74a6ea2f 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 b9ed1288ce2de87cb32de899b303e1c31565916e..985608634f8cccf9eefd32328c96fbbcb2ae0079 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 5aa5e8146496ba807ece60fe5b287d70a79a8ad4..c3c4c266b8465981b658676617fa79202aa4396d 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 4e01c57d8c8de349da61e80cb0e4cb0f4aece3fd..a1f66e2c9a8694c9d83471dd63f6659d431840eb 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 dcafbda3e5be1035034d61c486f4eefceb6887f6..ec96130533cc54630c3f26f6253e58b5a0f5a7cf 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 fcf2761d8828804d3edf7ca8e2ad245576d23685..fdace204b0549aa2599edd67c1c3112e0c4bf9de 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 e622cc1f96ffe58336ee8c24f12c54e1679e11b7..814d0eca9b334ea86c862bc617a46f137f04d475 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 5ecf267dc4cc8da779112356057cb8253d27e7cb..3efbb35267c853d576cc3a4d3104ec4ba1a18d1d 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 9199adf32d33c1639dfa2354e609ef92b638ef91..efa8a151d78907d4b17d5f5dd07eaed9b1f2c02c 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 00c5be8c55b8ff7186a670645cb654dc46779399..a9e068423ba0651414cc0c5b23f0eda6937a6c23 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 7fb0edfe3d2482514f32f48afef1a5a1c46e9805..dbcd5262c0167c1ae0dacf7dd578b5c3a2a43ce5 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 edf3e8a0ff839c069bc05154f85c1e68e89544f2..0075686276aa7f703a6fa63856065a971b5d8388 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 eaecaadfa8c56436994c2afbe9a70223467ceec2..abde97471636918a6b7e2381634cdc743678d24c 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 b2793b91cc553e41e80170ac791d10b83f0e776d..4cbebf3d80eb1492d847f1ad8a9888e2a6c17b97 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 aedd5736a87d53862fa2be4176e9762fcd8df18a..8d3945ab7334840684db42ea6eefa9bafc52c061 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 00cd36e0860105aaa577d089e8a169e7d7b1fd97..61088a6a94245144f46fa42e92fdce9eb07f1599 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 fda26679f7d5c8cac8780c55a470357a2f16ec01..194928214606e160932fcb335f604421431b6eb2 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 6ca30739625f7ac568d693b5d4b9d3d9e47b46de..8675d26a678ba39cce44359453681f65e654022c 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 9c7194b26ee2dbf3aa3dd1b1f19b3de290d13f57..80f6d127257adbbcd590eadbdcafeab95c42f38f 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 2231d93cc7ad116e55c77e0269fa27878f6c6a4d..64a61b286b2c959fbb67c72dcc098199d74ff68d 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 bc0f5fb66e249dc2e652b4126f9b907707b87c6c..40a9f5ed814dd16defab7915d4368266cd5fa1e4 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 5b44e2e46dafbec0855fe682a1e06c6e91ad7def..35e514dc7b7d7148cbed9198b0d3348e6db88a62 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 7b12acf210f81cd408a4b0e8a965fd4441165c9f..37b6ad1f9a1bf6697fb813d5e5c1ebd0bea48d15 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 0951f7aca1eff6671f187d03b203952926a1ecea..822616e3c3754118ab2e09eada44d59a24c3954c 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 5a0f04c2c813661991047174c7a2643e74d694bd..27ffe0ebf0a686793c30f25f033895bf70ddfe51 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 09810ddd11ec3231bb34c6e79e3235c24bbcc546..b1cb0ffb15c70d1970f3cbebab28ff7f8e8530ce 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 8d4ccd35a01692d911f50d91bfe9bee0d89d883f..8a7a23a84ac5c3b6a7b7d4979e8c5866097895a5 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 90d41d26ec6d8c37f04682aa05b8731a4d4c3002..7536a4c01293a9b3e97bf1171941b6724213ad6c 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 e78802e75ea6cb358d6e3bec8d3a25efcffc66d4..bcc224a83734cdb49c25993066babe904a42d32f 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 6f10b496472662c2550d943db16dac9dcb6dda4d..2cbe1c24999660a198ededf163584d9cc9e51bdd 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 0632d34905c73811456594cf0bf5e27f8710f5c7..c6e46d6e9f752040e2a6aa48588466556fbc09e9 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 7bbd318bc93e0bb062ed378b4aa6e3bd3b2f2a44..befa45f809c32976470d2a9f072a5580a578f791 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 ceeb53737f86ba195b13b69933234313e8dcb506..66bc03bdb138c36cd8f717b76fe7bc9cd57cbbb3 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 9dccb1edfd2aba2070023f4ae874bac0cc432293..dc76670c2f2a16c244d0ec58a779a8742d0e6c3e 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 74630e94fa3bc323b9528b691514d7456b9abd6d..d6447b3f7409d2b19a5c5186f017780bb29080b8 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 69b482bce7d2449688fc67731cf923ce47ffcc32..508e4359338bc385dc2a0901e24a6351f735f780 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,20 +162,113 @@ 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,
@@ -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 5f66e30d98239651283ec200cbf3c842e60eb2ae..82ee6ed954cb84272aa41be0fa3ad12eb6c64e2c 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 6574eb8766f90997c38d0ea56d67ad542ecf99b3..34b0de09d88190a04e30d867e31ad001f35f0e34 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 7cb105c103fe9408eb7c02b96dac4f4bfb702456..782e38bfc1eeea38215492aee5aaa587cc534525 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 f74786aa37be3cca7a4020cc82b08f0777aec4f5..e15ec2b12035aa06b4662c284a7bb3abda148eff 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 f3fce412c0c1a38bb7a5522ebf0d2dd2139bbff5..51073721e22400ef8a553ff153d240cf55789a68 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 90a429b7ebad8497d317639389c041d534366255..8494bb53ebdc9f33abee1d8e5a27f7d0dea3cc17 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 bf7c734259ad640f235b3be752b8f9f38e59fd80..7bab4de658a91d9fb1231f5e45461a268efc8487 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 3118d7506734267c8fcca7e80ad9eeba9269fce1..edae50b52806f5382fc6ee7296ac810fda2eb9b5 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 0b1cc516e778c912e77d341a90f4921e5227a258..97ac8c87cba232646f5f82617ea396b8c2573870 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 f8d59c7b90821a69b3401a7ec143bd6d8e0fc367..9e86a811086f6bf2e8316afdecdd29a4ec5195c9 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 ce86f158423bdc3861dc4547242eb37fc8210930..ba200b24be64cd307483fad13fef4a9283a93784 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 b07f164d65cf582a63c2ddfbfccaced509a6ceca..20e49095db2ae2ba0c2d7d83071aa2a14a81252d 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 11f53589a3f34879b6ab3e8d9062e294fb6f7260..d39b79f5e841ada25c0e1bf435e7c7fd926c15ea 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 fca624322dc8886f991632d7a2d5e78b0bdfa114..2af667beb2738ea498c87ed171d8e4aed5e31bd5 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 4dbc294df7e39eb9ccedf3abe1a6613a45060612..57fc5f459d0aa6a40719407b8c287ac7f3786d00 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 6f899c6926474ba2f60cd642399e10d61814ec3d..7c1845221e1cc08a0904b35a491e64b1cd70b953 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 e7a38d844a6a4e7a2e9ba738e56606d946fea47a..60a5da53668f54f9987692ccf7389794d13ea851 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 90b8342d1ed4bd2e95389541f299d49d59261e6a..8824610c21fb3a3476a281f6b3435213ebef12cd 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 9ea24f1cba73f812de0b3352806e89434c0d59f9..a2c9a5dbac6b0317fba5131ee212d3ef9292c77a 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 d8643ebabd3001b6a00544157a65a9bc9f3265c5..710192ed27ed3118656f1a3b08bc41ec92c649a1 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 7e4c2524b63052006650ff76b92b3b839d6c9cd6..b5656fce4ff5f042b9053258e31c9b503802d217 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 307bc0ddff99091a1f224bb236013a1a90b5fa76..ca115f33746f228bc4f9007e0397b567f644e2a8 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 c00687e05688e6498b70131ba11c1105d0b933b1..1217c52ab28e6b07847217890bb6f1d9840a84b1 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 5b84f7ae0b1e3820cdee9737f9e4bacd8ed7b8d3..ef44a2da644d47be1613041703093cf7567f8979 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 f8c3a10510c22b01eb06d9b51310562cb0c8b7dd..de9eb2cfbf4b5784c36a97da2748acd357250c37 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 0a2844c48a604a6fcf40c3f53e464f645f0d556d..fd30cddd58821f9c602a3bba0fcb91bbfc88aeef 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 d7a974532909136c44eec23520e50d209db5df4b..4a2293041821ff708c6cefef86ed21406f18266c 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 668dd27616a0c83f9a4a39b94de2ad22b8e98a2b..cc6a0a586f0b748c054c4c0e8631ea0d706501cb 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 ef8c98e21098479d231df5844fb2abab8ddab04f..f499efc6abcf8bed36c46c7aaa342c8b8be3c878 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 de0df86704e714778891b7fb1af6b2cfc1340bef..9df7bc91a26f54c9812718e481c538895aa5b4f8 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 fbad00a5abc83502c3a16a7762f05bf6dcccf36a..aeaea0e3b4c414ae925b79a3eb4962f7bb36ad4b 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 f80f30b6160e7f4be38811198e76c245f26e08ca..c8385ec77a86af678b36cf28a5d7c2fa8448de84 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 220af4fe0fc65b18b575c81025c436d303f01bbd..81ac001ee74187d325f7a7d166666f9ec3c497fa 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 9d7c9d354d34aeb9e0b3e4bf854fc6a999671958..78e8a6666cc6edad81bd87c98dcf0353af371866 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 33fa9432b241b353c3ae381ab57c48d5d71f763b..03688aa14e8adb8575163e3a40aeda4a70a32c19 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 1576104e3d9531ada721dc41ec5740f021345b83..b44a31523461e2c5ba7d768c941fc0934abaacb6 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 7d66018a2e33060d1bdc1a96b970fd61f6480e4a..2181ee283d823e19dafaa5b1b8a09fcb1b4f2b1a 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 58a6013712d2d2c7e063282e056fa31e2dd7a955..2675ca7f8d146ca579a7a4bb4c1397f641a95ee0 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 f084412eee0b7cdeced4f2b21b4140b01d1b141e..c8e029df770e38cac9a52dccb660666bd8cc9f74 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 1cfe5a738c479e53e3dd5ba21f460096d2a16e53..92f76d655e6cc1206e46c4fc0e60b147b62280e0 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 1c70b8d092270ba3a456664aaf6ec3e9da4b3b59..edf5b7a24900ec1edf8d1fbb1e426b5268d2d3e2 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 5dd0ccc70b863ea15fad25d739adebd39ce09dbd..13eaeed03898288d43abf107090346d513132820 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 41a16d30c79c5be46f89229d3185a033d8131f0f..e05d9b4c8317c44df17b9ca0c2dea0577741b1a6 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 c5738f14c4ba21b7a60453ab282309089134bdc2..776aff3678ff23bddda925dfbb48bde925cb7b82 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 ae152280e071621df4b9ccb93a4d614ed4ec50b1..a8cc736b5063bcc51d3a5d7ef3a6a737a2d6338e 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 997df03a0c2e22abd46bd274bf13f4b1e4f81f01..a81ceb61d746fb392f9eb30ca6ecac81dda04eda 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 080b1fcae5fa8f3f2b376d5b77bffab2519ddf57..9dd92a700442a4c7e3aa8b2bacc97830b4a95148 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 a0935987fa3a3ca22dfa0298d7bef054408830e6..7f40ab8e1bd809d017d6dff41b68b815c7ade240 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 7c157857f5cee925e796a49396a5843a758a01cd..2183e79783995eea92cb8d8498d0a8582c43c820 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 50590b1420a516863845249c96f689b28e766964..a5d38e8ad9e4925cabd9f90744befd6b9a35d522 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 9a78e3daf74264fa13f446a4779d342ab6341488..ff784072fb4233a29a46a55bf21d75e094bb8d5c 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 ae13fb94b2e8d7b82a259cdc830643cb2e144901..2ffc7298f686ec6002ee54dd210ebc7e8b13b757 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 393685390f3ee41f78a903d198eb51484654a55f..e26312fb4356720b06e3f844423a96ca07dd79e4 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 25e50ffc44ec8b42fd35b4ab0b60a08ee09edd7d..1bc21ccfa71b85f671afebb8d2826e43be45f9e9 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 945ddecf90c9a3b6c6bcd916c3c1d293ac51dc15..0eb0f4ae592054f7ebf1ee9182169fc57adbc11c 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 5061f1db3f021072b32a0617385ccb75db427d07..92d38ab3c60e87861f992411e3774aeceed30f56 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 222d2e792ca6d259885fa6da3a8ad792bbb338ad..27efbcdac6a979875976a7a74d1c15e07c1afa49 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 d224dc3bb092b0ef04545cc57891a7bdb4de4b2f..0c65386fa30d5cecda61d9d4ca290b6577df2f59 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 7ef0b4a181e130883a741c088fc9cb7668ddf231..84d94f572a463b294ed542476eb22e922eab81b9 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 b78ee10a956a199e8d5e1a1615d5a6e1cbe94acd..2329cccf1fa6dd15f65c4dc30c54c62cce0551a7 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 d85e66979711cbe62a168d9012f1a40ed2d5c405..e59acb1daa2355efd56e4398ab82d0c0f8647ab4 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 1cb6e51e6bda97aadb4ba88bd46123544dcc4140..170e8e60cdb7fe3d47307d4b9146039e3af7f3a8 100644 (file)
@@ -141,6 +141,24 @@ void ntb_unregister_event_callback(struct ntb_device *ndev)
        ndev->event_cb = NULL;
 }
 
+static void ntb_irq_work(unsigned long data)
+{
+       struct ntb_db_cb *db_cb = (struct ntb_db_cb *)data;
+       int rc;
+
+       rc = db_cb->callback(db_cb->data, db_cb->db_num);
+       if (rc)
+               tasklet_schedule(&db_cb->irq_work);
+       else {
+               struct ntb_device *ndev = db_cb->ndev;
+               unsigned long mask;
+
+               mask = readw(ndev->reg_ofs.ldb_mask);
+               clear_bit(db_cb->db_num * ndev->bits_per_vector, &mask);
+               writew(mask, ndev->reg_ofs.ldb_mask);
+       }
+}
+
 /**
  * ntb_register_db_callback() - register a callback for doorbell interrupt
  * @ndev: pointer to ntb_device instance
@@ -155,7 +173,7 @@ void ntb_unregister_event_callback(struct ntb_device *ndev)
  * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
  */
 int ntb_register_db_callback(struct ntb_device *ndev, unsigned int idx,
-                            void *data, void (*func)(void *data, int db_num))
+                            void *data, int (*func)(void *data, int db_num))
 {
        unsigned long mask;
 
@@ -166,6 +184,10 @@ int ntb_register_db_callback(struct ntb_device *ndev, unsigned int idx,
 
        ndev->db_cb[idx].callback = func;
        ndev->db_cb[idx].data = data;
+       ndev->db_cb[idx].ndev = ndev;
+
+       tasklet_init(&ndev->db_cb[idx].irq_work, ntb_irq_work,
+                    (unsigned long) &ndev->db_cb[idx]);
 
        /* unmask interrupt */
        mask = readw(ndev->reg_ofs.ldb_mask);
@@ -194,6 +216,8 @@ void ntb_unregister_db_callback(struct ntb_device *ndev, unsigned int idx)
        set_bit(idx * ndev->bits_per_vector, &mask);
        writew(mask, ndev->reg_ofs.ldb_mask);
 
+       tasklet_disable(&ndev->db_cb[idx].irq_work);
+
        ndev->db_cb[idx].callback = NULL;
 }
 
@@ -678,6 +702,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
                                return -EINVAL;
 
                        ndev->limits.max_mw = SNB_ERRATA_MAX_MW;
+                       ndev->limits.max_db_bits = SNB_MAX_DB_BITS;
                        ndev->reg_ofs.spad_write = ndev->mw[1].vbase +
                                                   SNB_SPAD_OFFSET;
                        ndev->reg_ofs.rdb = ndev->mw[1].vbase +
@@ -688,8 +713,21 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
                         */
                        writeq(ndev->mw[1].bar_sz + 0x1000, ndev->reg_base +
                               SNB_PBAR4LMT_OFFSET);
+                       /* HW errata on the Limit registers.  They can only be
+                        * written when the base register is 4GB aligned and
+                        * < 32bit.  This should already be the case based on the
+                        * driver defaults, but write the Limit registers first
+                        * just in case.
+                        */
                } else {
                        ndev->limits.max_mw = SNB_MAX_MW;
+
+                       /* HW Errata on bit 14 of b2bdoorbell register.  Writes
+                        * will not be mirrored to the remote system.  Shrink
+                        * the number of bits by one, since bit 14 is the last
+                        * bit.
+                        */
+                       ndev->limits.max_db_bits = SNB_MAX_DB_BITS - 1;
                        ndev->reg_ofs.spad_write = ndev->reg_base +
                                                   SNB_B2B_SPAD_OFFSET;
                        ndev->reg_ofs.rdb = ndev->reg_base +
@@ -699,6 +737,12 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
                         * something silly
                         */
                        writeq(0, ndev->reg_base + SNB_PBAR4LMT_OFFSET);
+                       /* HW errata on the Limit registers.  They can only be
+                        * written when the base register is 4GB aligned and
+                        * < 32bit.  This should already be the case based on the
+                        * driver defaults, but write the Limit registers first
+                        * just in case.
+                        */
                }
 
                /* The Xeon errata workaround requires setting SBAR Base
@@ -769,6 +813,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
                 * have an equal amount.
                 */
                ndev->limits.max_spads = SNB_MAX_COMPAT_SPADS / 2;
+               ndev->limits.max_db_bits = SNB_MAX_DB_BITS;
                /* Note: The SDOORBELL is the cause of the errata.  You REALLY
                 * don't want to touch it.
                 */
@@ -793,6 +838,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
                 * have an equal amount.
                 */
                ndev->limits.max_spads = SNB_MAX_COMPAT_SPADS / 2;
+               ndev->limits.max_db_bits = SNB_MAX_DB_BITS;
                ndev->reg_ofs.rdb = ndev->reg_base + SNB_PDOORBELL_OFFSET;
                ndev->reg_ofs.ldb = ndev->reg_base + SNB_SDOORBELL_OFFSET;
                ndev->reg_ofs.ldb_mask = ndev->reg_base + SNB_SDBMSK_OFFSET;
@@ -819,7 +865,6 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
        ndev->reg_ofs.lnk_stat = ndev->reg_base + SNB_SLINK_STATUS_OFFSET;
        ndev->reg_ofs.spci_cmd = ndev->reg_base + SNB_PCICMD_OFFSET;
 
-       ndev->limits.max_db_bits = SNB_MAX_DB_BITS;
        ndev->limits.msix_cnt = SNB_MSIX_CNT;
        ndev->bits_per_vector = SNB_DB_BITS_PER_VEC;
 
@@ -934,12 +979,16 @@ static irqreturn_t bwd_callback_msix_irq(int irq, void *data)
 {
        struct ntb_db_cb *db_cb = data;
        struct ntb_device *ndev = db_cb->ndev;
+       unsigned long mask;
 
        dev_dbg(&ndev->pdev->dev, "MSI-X irq %d received for DB %d\n", irq,
                db_cb->db_num);
 
-       if (db_cb->callback)
-               db_cb->callback(db_cb->data, db_cb->db_num);
+       mask = readw(ndev->reg_ofs.ldb_mask);
+       set_bit(db_cb->db_num * ndev->bits_per_vector, &mask);
+       writew(mask, ndev->reg_ofs.ldb_mask);
+
+       tasklet_schedule(&db_cb->irq_work);
 
        /* No need to check for the specific HB irq, any interrupt means
         * we're connected.
@@ -955,12 +1004,16 @@ static irqreturn_t xeon_callback_msix_irq(int irq, void *data)
 {
        struct ntb_db_cb *db_cb = data;
        struct ntb_device *ndev = db_cb->ndev;
+       unsigned long mask;
 
        dev_dbg(&ndev->pdev->dev, "MSI-X irq %d received for DB %d\n", irq,
                db_cb->db_num);
 
-       if (db_cb->callback)
-               db_cb->callback(db_cb->data, db_cb->db_num);
+       mask = readw(ndev->reg_ofs.ldb_mask);
+       set_bit(db_cb->db_num * ndev->bits_per_vector, &mask);
+       writew(mask, ndev->reg_ofs.ldb_mask);
+
+       tasklet_schedule(&db_cb->irq_work);
 
        /* On Sandybridge, there are 16 bits in the interrupt register
         * but only 4 vectors.  So, 5 bits are assigned to the first 3
@@ -986,7 +1039,7 @@ static irqreturn_t xeon_event_msix_irq(int irq, void *dev)
                dev_err(&ndev->pdev->dev, "Error determining link status\n");
 
        /* bit 15 is always the link bit */
-       writew(1 << ndev->limits.max_db_bits, ndev->reg_ofs.ldb);
+       writew(1 << SNB_LINK_DB, ndev->reg_ofs.ldb);
 
        return IRQ_HANDLED;
 }
@@ -1075,6 +1128,10 @@ static int ntb_setup_msix(struct ntb_device *ndev)
                         "Only %d MSI-X vectors.  Limiting the number of queues to that number.\n",
                         rc);
                msix_entries = rc;
+
+               rc = pci_enable_msix(pdev, ndev->msix_entries, msix_entries);
+               if (rc)
+                       goto err1;
        }
 
        for (i = 0; i < msix_entries; i++) {
@@ -1176,9 +1233,10 @@ static int ntb_setup_interrupts(struct ntb_device *ndev)
         */
        if (ndev->hw_type == BWD_HW)
                writeq(~0, ndev->reg_ofs.ldb_mask);
-       else
-               writew(~(1 << ndev->limits.max_db_bits),
-                      ndev->reg_ofs.ldb_mask);
+       else {
+               u16 var = 1 << SNB_LINK_DB;
+               writew(~var, ndev->reg_ofs.ldb_mask);
+       }
 
        rc = ntb_setup_msix(ndev);
        if (!rc)
@@ -1286,6 +1344,39 @@ static void ntb_free_debugfs(struct ntb_device *ndev)
        }
 }
 
+static void ntb_hw_link_up(struct ntb_device *ndev)
+{
+       if (ndev->conn_type == NTB_CONN_TRANSPARENT)
+               ntb_link_event(ndev, NTB_LINK_UP);
+       else {
+               u32 ntb_cntl;
+
+               /* Let's bring the NTB link up */
+               ntb_cntl = readl(ndev->reg_ofs.lnk_cntl);
+               ntb_cntl &= ~(NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK);
+               ntb_cntl |= NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP;
+               ntb_cntl |= NTB_CNTL_P2S_BAR45_SNOOP | NTB_CNTL_S2P_BAR45_SNOOP;
+               writel(ntb_cntl, ndev->reg_ofs.lnk_cntl);
+       }
+}
+
+static void ntb_hw_link_down(struct ntb_device *ndev)
+{
+       u32 ntb_cntl;
+
+       if (ndev->conn_type == NTB_CONN_TRANSPARENT) {
+               ntb_link_event(ndev, NTB_LINK_DOWN);
+               return;
+       }
+
+       /* Bring NTB link down */
+       ntb_cntl = readl(ndev->reg_ofs.lnk_cntl);
+       ntb_cntl &= ~(NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP);
+       ntb_cntl &= ~(NTB_CNTL_P2S_BAR45_SNOOP | NTB_CNTL_S2P_BAR45_SNOOP);
+       ntb_cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK;
+       writel(ntb_cntl, ndev->reg_ofs.lnk_cntl);
+}
+
 static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct ntb_device *ndev;
@@ -1374,9 +1465,7 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (rc)
                goto err6;
 
-       /* Let's bring the NTB link up */
-       writel(NTB_CNTL_BAR23_SNOOP | NTB_CNTL_BAR45_SNOOP,
-              ndev->reg_ofs.lnk_cntl);
+       ntb_hw_link_up(ndev);
 
        return 0;
 
@@ -1406,12 +1495,8 @@ static void ntb_pci_remove(struct pci_dev *pdev)
 {
        struct ntb_device *ndev = pci_get_drvdata(pdev);
        int i;
-       u32 ntb_cntl;
 
-       /* Bring NTB link down */
-       ntb_cntl = readl(ndev->reg_ofs.lnk_cntl);
-       ntb_cntl |= NTB_CNTL_LINK_DISABLE;
-       writel(ntb_cntl, ndev->reg_ofs.lnk_cntl);
+       ntb_hw_link_down(ndev);
 
        ntb_transport_free(ndev->ntb_transport);
 
index 0a31cedae7d42f9227d125bfabe97297b47255d9..bbdb7edca10cd8647e4b739dfab29f1ed33fc1e6 100644 (file)
@@ -106,10 +106,11 @@ struct ntb_mw {
 };
 
 struct ntb_db_cb {
-       void (*callback) (void *data, int db_num);
+       int (*callback)(void *data, int db_num);
        unsigned int db_num;
        void *data;
        struct ntb_device *ndev;
+       struct tasklet_struct irq_work;
 };
 
 struct ntb_device {
@@ -228,8 +229,8 @@ struct ntb_device *ntb_register_transport(struct pci_dev *pdev,
 void ntb_unregister_transport(struct ntb_device *ndev);
 void ntb_set_mw_addr(struct ntb_device *ndev, unsigned int mw, u64 addr);
 int ntb_register_db_callback(struct ntb_device *ndev, unsigned int idx,
-                            void *data, void (*db_cb_func) (void *data,
-                                                            int db_num));
+                            void *data, int (*db_cb_func)(void *data,
+                                                          int db_num));
 void ntb_unregister_db_callback(struct ntb_device *ndev, unsigned int idx);
 int ntb_register_event_callback(struct ntb_device *ndev,
                                void (*event_cb_func) (void *handle,
index aa4bdd393c58ee2c4cf40105b1644afdcb19913c..9774506419d75ba1a5dc08a5f0e4d52e90d33ad7 100644 (file)
@@ -55,6 +55,7 @@
 #define SNB_MAX_COMPAT_SPADS   16
 /* Reserve the uppermost bit for link interrupt */
 #define SNB_MAX_DB_BITS                15
+#define SNB_LINK_DB            15
 #define SNB_DB_BITS_PER_VEC    5
 #define SNB_MAX_MW             2
 #define SNB_ERRATA_MAX_MW      1
@@ -75,9 +76,6 @@
 #define SNB_SBAR2XLAT_OFFSET   0x0030
 #define SNB_SBAR4XLAT_OFFSET   0x0038
 #define SNB_SBAR0BASE_OFFSET   0x0040
-#define SNB_SBAR0BASE_OFFSET   0x0040
-#define SNB_SBAR2BASE_OFFSET   0x0048
-#define SNB_SBAR4BASE_OFFSET   0x0050
 #define SNB_SBAR2BASE_OFFSET   0x0048
 #define SNB_SBAR4BASE_OFFSET   0x0050
 #define SNB_NTBCNTL_OFFSET     0x0058
 #define BWD_LTSSMSTATEJMP_FORCEDETECT  (1 << 2)
 #define BWD_IBIST_ERR_OFLOW    0x7FFF7FFF
 
-#define NTB_CNTL_CFG_LOCK      (1 << 0)
-#define NTB_CNTL_LINK_DISABLE  (1 << 1)
-#define NTB_CNTL_BAR23_SNOOP   (1 << 2)
-#define NTB_CNTL_BAR45_SNOOP   (1 << 6)
-#define BWD_CNTL_LINK_DOWN     (1 << 16)
+#define NTB_CNTL_CFG_LOCK              (1 << 0)
+#define NTB_CNTL_LINK_DISABLE          (1 << 1)
+#define NTB_CNTL_S2P_BAR23_SNOOP       (1 << 2)
+#define NTB_CNTL_P2S_BAR23_SNOOP       (1 << 4)
+#define NTB_CNTL_S2P_BAR45_SNOOP       (1 << 6)
+#define NTB_CNTL_P2S_BAR45_SNOOP       (1 << 8)
+#define BWD_CNTL_LINK_DOWN             (1 << 16)
 
 #define NTB_PPD_OFFSET         0x00D4
 #define SNB_PPD_CONN_TYPE      0x0003
index 12a9e83c008b402f0f7acde4e80c742c7795348e..3217f394d45b106051b282f824be1413b5efa65d 100644 (file)
@@ -119,7 +119,6 @@ struct ntb_transport_qp {
 
        void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data,
                            void *data, int len);
-       struct tasklet_struct rx_work;
        struct list_head rx_pend_q;
        struct list_head rx_free_q;
        spinlock_t ntb_rx_pend_q_lock;
@@ -584,11 +583,8 @@ static int ntb_set_mw(struct ntb_transport *nt, int num_mw, unsigned int size)
        return 0;
 }
 
-static void ntb_qp_link_cleanup(struct work_struct *work)
+static void ntb_qp_link_cleanup(struct ntb_transport_qp *qp)
 {
-       struct ntb_transport_qp *qp = container_of(work,
-                                                  struct ntb_transport_qp,
-                                                  link_cleanup);
        struct ntb_transport *nt = qp->transport;
        struct pci_dev *pdev = ntb_query_pdev(nt->ndev);
 
@@ -602,6 +598,16 @@ static void ntb_qp_link_cleanup(struct work_struct *work)
 
        dev_info(&pdev->dev, "qp %d: Link Down\n", qp->qp_num);
        qp->qp_link = NTB_LINK_DOWN;
+}
+
+static void ntb_qp_link_cleanup_work(struct work_struct *work)
+{
+       struct ntb_transport_qp *qp = container_of(work,
+                                                  struct ntb_transport_qp,
+                                                  link_cleanup);
+       struct ntb_transport *nt = qp->transport;
+
+       ntb_qp_link_cleanup(qp);
 
        if (nt->transport_link == NTB_LINK_UP)
                schedule_delayed_work(&qp->link_work,
@@ -613,22 +619,20 @@ static void ntb_qp_link_down(struct ntb_transport_qp *qp)
        schedule_work(&qp->link_cleanup);
 }
 
-static void ntb_transport_link_cleanup(struct work_struct *work)
+static void ntb_transport_link_cleanup(struct ntb_transport *nt)
 {
-       struct ntb_transport *nt = container_of(work, struct ntb_transport,
-                                               link_cleanup);
        int i;
 
+       /* Pass along the info to any clients */
+       for (i = 0; i < nt->max_qps; i++)
+               if (!test_bit(i, &nt->qp_bitmap))
+                       ntb_qp_link_cleanup(&nt->qps[i]);
+
        if (nt->transport_link == NTB_LINK_DOWN)
                cancel_delayed_work_sync(&nt->link_work);
        else
                nt->transport_link = NTB_LINK_DOWN;
 
-       /* Pass along the info to any clients */
-       for (i = 0; i < nt->max_qps; i++)
-               if (!test_bit(i, &nt->qp_bitmap))
-                       ntb_qp_link_down(&nt->qps[i]);
-
        /* The scratchpad registers keep the values if the remote side
         * goes down, blast them now to give them a sane value the next
         * time they are accessed
@@ -637,6 +641,14 @@ static void ntb_transport_link_cleanup(struct work_struct *work)
                ntb_write_local_spad(nt->ndev, i, 0);
 }
 
+static void ntb_transport_link_cleanup_work(struct work_struct *work)
+{
+       struct ntb_transport *nt = container_of(work, struct ntb_transport,
+                                               link_cleanup);
+
+       ntb_transport_link_cleanup(nt);
+}
+
 static void ntb_transport_event_callback(void *data, enum ntb_hw_event event)
 {
        struct ntb_transport *nt = data;
@@ -880,7 +892,7 @@ static int ntb_transport_init_queue(struct ntb_transport *nt,
        }
 
        INIT_DELAYED_WORK(&qp->link_work, ntb_qp_link_work);
-       INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup);
+       INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup_work);
 
        spin_lock_init(&qp->ntb_rx_pend_q_lock);
        spin_lock_init(&qp->ntb_rx_free_q_lock);
@@ -936,7 +948,7 @@ int ntb_transport_init(struct pci_dev *pdev)
        }
 
        INIT_DELAYED_WORK(&nt->link_work, ntb_transport_link_work);
-       INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup);
+       INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup_work);
 
        rc = ntb_register_event_callback(nt->ndev,
                                         ntb_transport_event_callback);
@@ -972,7 +984,7 @@ void ntb_transport_free(void *transport)
        struct ntb_device *ndev = nt->ndev;
        int i;
 
-       nt->transport_link = NTB_LINK_DOWN;
+       ntb_transport_link_cleanup(nt);
 
        /* verify that all the qp's are freed */
        for (i = 0; i < nt->max_qps; i++) {
@@ -1034,10 +1046,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 +1056,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;
+
+       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;
 
-       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);
+       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 +1106,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.
@@ -1175,11 +1200,14 @@ err:
        goto out;
 }
 
-static void ntb_transport_rx(unsigned long data)
+static int ntb_transport_rxc_db(void *data, int db_num)
 {
-       struct ntb_transport_qp *qp = (struct ntb_transport_qp *)data;
+       struct ntb_transport_qp *qp = data;
        int rc, i;
 
+       dev_dbg(&ntb_query_pdev(qp->ndev)->dev, "%s: doorbell %d received\n",
+               __func__, db_num);
+
        /* Limit the number of packets processed in a single interrupt to
         * provide fairness to others
         */
@@ -1191,16 +1219,8 @@ static void ntb_transport_rx(unsigned long data)
 
        if (qp->dma_chan)
                dma_async_issue_pending(qp->dma_chan);
-}
-
-static void ntb_transport_rxc_db(void *data, int db_num)
-{
-       struct ntb_transport_qp *qp = data;
-
-       dev_dbg(&ntb_query_pdev(qp->ndev)->dev, "%s: doorbell %d received\n",
-               __func__, db_num);
 
-       tasklet_schedule(&qp->rx_work);
+       return i;
 }
 
 static void ntb_tx_copy_callback(void *data)
@@ -1245,12 +1265,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 +1293,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++;
@@ -1406,11 +1439,12 @@ ntb_transport_create_queue(void *data, struct pci_dev *pdev,
        qp->tx_handler = handlers->tx_handler;
        qp->event_handler = handlers->event_handler;
 
+       dmaengine_get();
        qp->dma_chan = dma_find_channel(DMA_MEMCPY);
-       if (!qp->dma_chan)
+       if (!qp->dma_chan) {
+               dmaengine_put();
                dev_info(&pdev->dev, "Unable to allocate DMA channel, using CPU instead\n");
-       else
-               dmaengine_get();
+       }
 
        for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
                entry = kzalloc(sizeof(struct ntb_queue_entry), GFP_ATOMIC);
@@ -1432,25 +1466,23 @@ ntb_transport_create_queue(void *data, struct pci_dev *pdev,
                             &qp->tx_free_q);
        }
 
-       tasklet_init(&qp->rx_work, ntb_transport_rx, (unsigned long) qp);
-
        rc = ntb_register_db_callback(qp->ndev, free_queue, qp,
                                      ntb_transport_rxc_db);
        if (rc)
-               goto err3;
+               goto err2;
 
        dev_info(&pdev->dev, "NTB Transport QP %d created\n", qp->qp_num);
 
        return qp;
 
-err3:
-       tasklet_disable(&qp->rx_work);
 err2:
        while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
                kfree(entry);
 err1:
        while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q)))
                kfree(entry);
+       if (qp->dma_chan)
+               dmaengine_put();
        set_bit(free_queue, &nt->qp_bitmap);
 err:
        return NULL;
@@ -1489,7 +1521,6 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp)
        }
 
        ntb_unregister_db_callback(qp->ndev, qp->qp_num);
-       tasklet_disable(&qp->rx_work);
 
        cancel_delayed_work_sync(&qp->link_work);
 
index dc82ef096f3ba61267484057b083caed92748d5b..2872ece81f358df7ff139c143e86d58031485b14 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 d4716273651eb4354a7834304bdf812e69f9c647..c864f82bd37de9a369ed82b790676de29a4d59fb 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 95655d7c0d0b1abdd7e2d4c584af7f0f4217d184..e52d7ffa38b95d2b78835f551e3c27dac4b6d36b 100644 (file)
@@ -410,7 +410,7 @@ EXPORT_SYMBOL_GPL(pci_disable_pasid);
  * Otherwise is returns a bitmask with supported features. Current
  * features reported are:
  * PCI_PASID_CAP_EXEC - Execute permission supported
- * PCI_PASID_CAP_PRIV - Priviledged mode supported
+ * PCI_PASID_CAP_PRIV - Privileged mode supported
  */
 int pci_pasid_features(struct pci_dev *pdev)
 {
index 7c4f38dd42ba6a5ef868e24e7b033998284fb4ec..0afbbbc55c81e4bca1ab5ef4f9b556fcad0dc6b1 100644 (file)
@@ -249,7 +249,7 @@ struct tegra_pcie {
        void __iomem *afi;
        int irq;
 
-       struct list_head busses;
+       struct list_head buses;
        struct resource *cs;
 
        struct resource io;
@@ -399,14 +399,14 @@ free:
 
 /*
  * Look up a virtual address mapping for the specified bus number. If no such
- * mapping existis, try to create one.
+ * mapping exists, try to create one.
  */
 static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie,
                                        unsigned int busnr)
 {
        struct tegra_pcie_bus *bus;
 
-       list_for_each_entry(bus, &pcie->busses, list)
+       list_for_each_entry(bus, &pcie->buses, list)
                if (bus->nr == busnr)
                        return (void __iomem *)bus->area->addr;
 
@@ -414,7 +414,7 @@ static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie,
        if (IS_ERR(bus))
                return NULL;
 
-       list_add_tail(&bus->list, &pcie->busses);
+       list_add_tail(&bus->list, &pcie->buses);
 
        return (void __iomem *)bus->area->addr;
 }
@@ -808,7 +808,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
        value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
        afi_writel(pcie, value, AFI_FUSE);
 
-       /* initialze internal PHY, enable up to 16 PCIE lanes */
+       /* initialize internal PHY, enable up to 16 PCIE lanes */
        pads_writel(pcie, 0x0, PADS_CTL_SEL);
 
        /* override IDDQ to 1 on all 4 lanes */
@@ -1624,7 +1624,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
        if (!pcie)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&pcie->busses);
+       INIT_LIST_HEAD(&pcie->buses);
        INIT_LIST_HEAD(&pcie->ports);
        pcie->soc_data = match->data;
        pcie->dev = &pdev->dev;
index 1e1fea4d959b9399e84867a8153f206d45097438..e33b68be03912e1a3659d8a5aa90aa9b2c70ef66 100644 (file)
@@ -197,7 +197,7 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
                        return -ENOSPC;
                /*
                 * Check if this position is at correct offset.nvec is always a
-                * power of two. pos0 must be nvec bit alligned.
+                * power of two. pos0 must be nvec bit aligned.
                 */
                if (pos % msgvec)
                        pos += msgvec - (pos % msgvec);
index 0a648af895315cdab34d3c090f51237a47755fea..df8caec597895da466f821d23c4ba0315fb495ee 100644 (file)
@@ -133,8 +133,8 @@ config HOTPLUG_PCI_RPA_DLPAR
 
          To compile this driver as a module, choose M here: the
          module will be called rpadlpar_io.
-         When in doubt, say N.
+
+         When in doubt, say N.
 
 config HOTPLUG_PCI_SGI
        tristate "SGI PCI Hotplug Support"
index 47ec8c80e16d0fabe925ecc0e78790fad1a68743..3e6532b945c1829663ca5d5b6ff9bdb24b944f16 100644 (file)
@@ -31,7 +31,7 @@ pci_hotplug-objs      +=      cpci_hotplug_core.o     \
                                cpci_hotplug_pci.o
 endif
 ifdef CONFIG_ACPI
-pci_hotplug-objs       +=      acpi_pcihp.o
+pci_hotplug-objs       +=      acpi_pcihp.o
 endif
 
 cpqphp-objs            :=      cpqphp_core.o   \
index 1ce8ee054f1aa89ba83fb1001761e9200262a666..a94d850ae228c377387d036fd0f3af7e656130c6 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 26100f510b1087f45bbe39b79dcf9649acf38e9d..1592dbe4f90461dbfa82be205f334eb0e8ecc6f6 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 8650d39db3922c74d36a01029068ca7b03c75935..dca66bc44578e661987e2356043e092e5097f14e 100644 (file)
@@ -111,7 +111,7 @@ int acpiphp_register_attention(struct acpiphp_attention_info *info)
  * @info: must match the pointer used to register
  *
  * Description: This is used to un-register a hardware specific acpi
- * driver that manipulates the attention LED.  The pointer to the 
+ * driver that manipulates the attention LED.  The pointer to the
  * info struct must be the same as the one used to set it.
  */
 int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
@@ -169,8 +169,8 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
  * was registered with us.  This allows hardware specific
  * ACPI implementations to blink the light for us.
  */
- static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
- {
+static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
+{
        int retval = -ENODEV;
 
        pr_debug("%s - physical_slot = %s\n", __func__,
@@ -182,8 +182,8 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
        } else
                attention_info = NULL;
        return retval;
- }
+}
+
 
 /**
  * get_power_status - get power status of a slot
@@ -323,7 +323,7 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
        if (retval) {
                pr_err("pci_hp_register failed with error %d\n", retval);
                goto error_hpslot;
-       }
+       }
 
        pr_info("Slot [%s] registered\n", slot_name(slot));
 
index 5b4e9eb0e8ff388a9e9beb4467cf9b0ce144ac04..1cf605f6767357947e9a097981caa7d7869ca2f0 100644 (file)
@@ -325,7 +325,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
 
        list_add_tail(&slot->node, &bridge->slots);
 
-       /* Register slots for ejectable funtions only. */
+       /* Register slots for ejectable functions only. */
        if (acpi_pci_check_ejectable(pbus, handle)  || is_dock_device(handle)) {
                unsigned long long sun;
                int retval;
index 0d64c414bf7876a74baf7acc4ebf6664c8bc7542..ecfac7e72d91ae29a3ec8a5b6e8d159d0f7b7178 100644 (file)
@@ -116,7 +116,7 @@ static struct bin_attribute ibm_apci_table_attr = {
            .read = ibm_read_apci_table,
            .write = NULL,
 };
-static struct acpiphp_attention_info ibm_attention_info = 
+static struct acpiphp_attention_info ibm_attention_info =
 {
        .set_attn = ibm_set_attention_status,
        .get_attn = ibm_get_attention_status,
@@ -171,9 +171,9 @@ ibm_slot_done:
  */
 static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
 {
-       union acpi_object args[2]; 
+       union acpi_object args[2];
        struct acpi_object_list params = { .pointer = args, .count = 2 };
-       acpi_status stat; 
+       acpi_status stat;
        unsigned long long rc;
        union apci_descriptor *ibm_slot;
 
@@ -208,7 +208,7 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
  *
  * Description: This method is registered with the acpiphp module as a
  * callback to do the device specific task of getting the LED status.
- * 
+ *
  * Because there is no direct method of getting the LED status directly
  * from an ACPI call, we read the aPCI table and parse out our
  * slot descriptor to read the status from that.
@@ -259,7 +259,7 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
        pr_debug("%s: Received notification %02x\n", __func__, event);
 
        if (subevent == 0x80) {
-               pr_debug("%s: generationg bus event\n", __func__);
+               pr_debug("%s: generating bus event\n", __func__);
                acpi_bus_generate_netlink_event(note->device->pnp.device_class,
                                                  dev_name(&note->device->dev),
                                                  note->event, detail);
@@ -387,7 +387,7 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
                u32 lvl, void *context, void **rv)
 {
        acpi_handle *phandle = (acpi_handle *)context;
-       acpi_status status; 
+       acpi_status status;
        struct acpi_device_info *info;
        int retval = 0;
 
@@ -405,7 +405,7 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
                        info->hardware_id.string, handle);
                *phandle = handle;
                /* returning non-zero causes the search to stop
-                * and returns this value to the caller of 
+                * and returns this value to the caller of
                 * acpi_walk_namespace, but it also causes some warnings
                 * in the acpi debug code to print...
                 */
index 2b4c412f94c36b181049e0389260d11cd4285fe3..00c81a3cefc9be63e6bf47c5af9bdda0eefde37f 100644 (file)
@@ -46,7 +46,7 @@
        do {                                                    \
                if (cpci_debug)                                 \
                        printk (KERN_DEBUG "%s: " format "\n",  \
-                               MY_NAME , ## arg);              \
+                               MY_NAME , ## arg);              \
        } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
index d8add34177f2cfa2725972bb3d57308d61b603df..d3add9819f633d5531c02958f05a1cd219044731 100644 (file)
@@ -39,7 +39,7 @@ extern int cpci_debug;
        do {                                                    \
                if (cpci_debug)                                 \
                        printk (KERN_DEBUG "%s: " format "\n",  \
-                               MY_NAME , ## arg);              \
+                               MY_NAME , ## arg);              \
        } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
index a6a71c41cdf8cab3e69f5436456cca36fdf0b5a7..7536eef620b0909b724cae3106dbfbc29d9dc2ec 100644 (file)
  * option) any later version.
  *
  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * You should have received a copy of the GNU General Public License along
@@ -53,9 +53,9 @@
 
 #define dbg(format, arg...)                                    \
        do {                                                    \
-               if(debug)                                       \
+               if (debug)                                      \
                        printk (KERN_DEBUG "%s: " format "\n",  \
-                               MY_NAME , ## arg);              \
+                               MY_NAME , ## arg);              \
        } while(0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
index 449b4bbc8301cafdd408777242300272cf27f295..e8c4a7ccf5788f73c0cf489ea09b0f0955c9e233 100644 (file)
  * option) any later version.
  *
  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * You should have received a copy of the GNU General Public License along
@@ -48,9 +48,9 @@
 
 #define dbg(format, arg...)                                    \
        do {                                                    \
-               if(debug)                                       \
+               if (debug)                                      \
                        printk (KERN_DEBUG "%s: " format "\n",  \
-                               MY_NAME , ## arg);              \
+                               MY_NAME , ## arg);              \
        } while(0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
@@ -285,7 +285,7 @@ static struct pci_device_id zt5550_hc_pci_tbl[] = {
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl);
-       
+
 static struct pci_driver zt5550_hc_driver = {
        .name           = "zt5550_hc",
        .id_table       = zt5550_hc_pci_tbl,
index bebc6060a558735c401dbb95d8e3852a92185dda..9a57fda5348cf2528c286ed7df364a413a1f671b 100644 (file)
  * option) any later version.
  *
  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * You should have received a copy of the GNU General Public License along
@@ -55,7 +55,7 @@
 #define HC_CMD_REG             0x0C
 #define ARB_CONFIG_GNT_REG     0x10
 #define ARB_CONFIG_CFG_REG     0x12
-#define ARB_CONFIG_REG         0x10
+#define ARB_CONFIG_REG         0x10
 #define ISOL_CONFIG_REG                0x18
 #define FAULT_STATUS_REG       0x20
 #define FAULT_CONFIG_REG       0x24
index c8eaeb43fa5d6518fa07682841bd640a8b994a46..31273e155e6cd360946f068fe7268d13c7e41c27 100644 (file)
@@ -862,10 +862,10 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_disable_device;
        }
 
-       /* Check for the proper subsystem ID's
+       /* Check for the proper subsystem IDs
         * Intel uses a different SSID programming model than Compaq.
         * For Intel, each SSID bit identifies a PHP capability.
-        * Also Intel HPC's may have RID=0.
+        * Also Intel HPCs may have RID=0.
         */
        if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) {
                err(msg_HPC_not_supported);
index d282019cda5fad4c3e4583eab78fe72017648cde..11845b7967995479d5fab311f0a6f4c53fb4a97f 100644 (file)
@@ -1231,7 +1231,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
 
        /* Only if mode change...*/
        if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
-               ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) 
+               ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
                        set_SOGO(ctrl);
 
        wait_for_ctrl_irq(ctrl);
@@ -1828,7 +1828,7 @@ static void interrupt_event_handler(struct controller *ctrl)
 
                                if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
                                        dbg("button pressed\n");
-                               } else if (ctrl->event_queue[loop].event_type == 
+                               } else if (ctrl->event_queue[loop].event_type ==
                                           INT_BUTTON_CANCEL) {
                                        dbg("button cancel\n");
                                        del_timer(&p_slot->task_event);
@@ -2411,11 +2411,11 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
                if (rc)
                        return rc;
 
-               /* find range of busses to use */
+               /* find range of buses to use */
                dbg("find ranges of buses to use\n");
                bus_node = get_max_resource(&(resources->bus_head), 1);
 
-               /* If we don't have any busses to allocate, we can't continue */
+               /* If we don't have any buses to allocate, we can't continue */
                if (!bus_node)
                        return -ENOMEM;
 
@@ -2900,7 +2900,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
 
                        /* If this function needs an interrupt and we are behind
                         * a bridge and the pin is tied to something that's
-                        * alread mapped, set this one the same */
+                        * already mapped, set this one the same */
                        if (temp_byte && resources->irqs &&
                            (resources->irqs->valid_INT &
                             (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
index 09801c6945ce48c2b732a2f480d4ff91007b3f5c..6e4a12c91adbaaf066093cef758b764485ddd8b4 100644 (file)
@@ -291,7 +291,7 @@ int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 s
  *
  * Reads configuration for all slots in a PCI bus and saves info.
  *
- * Note:  For non-hot plug busses, the slot # saved is the device #
+ * Note:  For non-hot plug buses, the slot # saved is the device #
  *
  * returns 0 if success
  */
@@ -455,7 +455,7 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
  * cpqhp_save_slot_config
  *
  * Saves configuration info for all PCI devices in a given slot
- * including subordinate busses.
+ * including subordinate buses.
  *
  * returns 0 if success
  */
@@ -1556,4 +1556,3 @@ void cpqhp_destroy_board_resources (struct pci_func * func)
                kfree(tres);
        }
 }
-
index 8c5b25871d02c86d3e3752669ad1e0c0d12d2fab..e3e46a7b3ee763374f534c431c0d7a54df67c8a2 100644 (file)
@@ -59,7 +59,7 @@ extern int ibmphp_debug;
 
 
 /************************************************************
-*  RESOURE TYPE                                             *
+*  RESOURCE TYPE                                             *
 ************************************************************/
 
 #define EBDA_RSRC_TYPE_MASK            0x03
@@ -103,7 +103,7 @@ extern int ibmphp_debug;
 //--------------------------------------------------------------
 
 struct rio_table_hdr {
-       u8 ver_num; 
+       u8 ver_num;
        u8 scal_count;
        u8 riodev_count;
        u16 offset;
@@ -127,7 +127,7 @@ struct scal_detail {
 };
 
 //--------------------------------------------------------------
-// RIO DETAIL 
+// RIO DETAIL
 //--------------------------------------------------------------
 
 struct rio_detail {
@@ -152,7 +152,7 @@ struct opt_rio {
        u8 first_slot_num;
        u8 middle_num;
        struct list_head opt_rio_list;
-};     
+};
 
 struct opt_rio_lo {
        u8 rio_type;
@@ -161,7 +161,7 @@ struct opt_rio_lo {
        u8 middle_num;
        u8 pack_count;
        struct list_head opt_rio_lo_list;
-};     
+};
 
 /****************************************************************
 *  HPC DESCRIPTOR NODE                                          *
@@ -574,7 +574,7 @@ void ibmphp_hpc_stop_poll_thread(void);
 #define HPC_CTLR_IRQ_PENDG     0x80
 
 //----------------------------------------------------------------------------
-// HPC_CTLR_WROKING status return codes
+// HPC_CTLR_WORKING status return codes
 //----------------------------------------------------------------------------
 #define HPC_CTLR_WORKING_NO    0x00
 #define HPC_CTLR_WORKING_YES   0x01
index cbd72d81d2536e5490a792d31fcdcf3055387fdd..efdc13adbe41fb26ea8ea466c082b17f7b64f940 100644 (file)
@@ -58,7 +58,7 @@ MODULE_DESCRIPTION (DRIVER_DESC);
 struct pci_bus *ibmphp_pci_bus;
 static int max_slots;
 
-static int irqs[16];    /* PIC mode IRQ's we're using so far (in case MPS
+static int irqs[16];    /* PIC mode IRQs we're using so far (in case MPS
                         * tables don't provide default info for empty slots */
 
 static int init_flag;
@@ -71,20 +71,20 @@ static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
        return get_max_adapter_speed_1 (hs, value, 1);
 }
 */
-static inline int get_cur_bus_info(struct slot **sl) 
+static inline int get_cur_bus_info(struct slot **sl)
 {
        int rc = 1;
        struct slot * slot_cur = *sl;
 
        debug("options = %x\n", slot_cur->ctrl->options);
-       debug("revision = %x\n", slot_cur->ctrl->revision);     
+       debug("revision = %x\n", slot_cur->ctrl->revision);
 
-       if (READ_BUS_STATUS(slot_cur->ctrl)) 
+       if (READ_BUS_STATUS(slot_cur->ctrl))
                rc = ibmphp_hpc_readslot(slot_cur, READ_BUSSTATUS, NULL);
-       
-       if (rc) 
+
+       if (rc)
                return rc;
-         
+
        slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED(slot_cur->busstatus);
        if (READ_BUS_MODE(slot_cur->ctrl))
                slot_cur->bus_on->current_bus_mode =
@@ -96,7 +96,7 @@ static inline int get_cur_bus_info(struct slot **sl)
                        slot_cur->busstatus,
                        slot_cur->bus_on->current_speed,
                        slot_cur->bus_on->current_bus_mode);
-       
+
        *sl = slot_cur;
        return 0;
 }
@@ -104,8 +104,8 @@ static inline int get_cur_bus_info(struct slot **sl)
 static inline int slot_update(struct slot **sl)
 {
        int rc;
-       rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL);
-       if (rc) 
+       rc = ibmphp_hpc_readslot(*sl, READ_ALLSTAT, NULL);
+       if (rc)
                return rc;
        if (!init_flag)
                rc = get_cur_bus_info(sl);
@@ -172,7 +172,7 @@ int ibmphp_init_devno(struct slot **cur_slot)
                        debug("(*cur_slot)->irq[3] = %x\n",
                                        (*cur_slot)->irq[3]);
 
-                       debug("rtable->exlusive_irqs = %x\n",
+                       debug("rtable->exclusive_irqs = %x\n",
                                        rtable->exclusive_irqs);
                        debug("rtable->slots[loop].irq[0].bitmap = %x\n",
                                        rtable->slots[loop].irq[0].bitmap);
@@ -271,7 +271,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
                        else
                                rc = -ENODEV;
                }
-       } else  
+       } else
                rc = -ENODEV;
 
        ibmphp_unlock_operations();
@@ -288,7 +288,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
 
        debug("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n",
                                        (ulong) hotplug_slot, (ulong) value);
-        
+
        ibmphp_lock_operations();
        if (hotplug_slot) {
                pslot = hotplug_slot->private;
@@ -406,14 +406,14 @@ static int get_max_bus_speed(struct slot *slot)
 
        ibmphp_lock_operations();
        mode = slot->supported_bus_mode;
-       speed = slot->supported_speed; 
+       speed = slot->supported_speed;
        ibmphp_unlock_operations();
 
        switch (speed) {
        case BUS_SPEED_33:
                break;
        case BUS_SPEED_66:
-               if (mode == BUS_MODE_PCIX) 
+               if (mode == BUS_MODE_PCIX)
                        speed += 0x01;
                break;
        case BUS_SPEED_100:
@@ -515,13 +515,13 @@ static int __init init_ops(void)
 
                debug("BEFORE GETTING SLOT STATUS, slot # %x\n",
                                                        slot_cur->number);
-               if (slot_cur->ctrl->revision == 0xFF) 
+               if (slot_cur->ctrl->revision == 0xFF)
                        if (get_ctrl_revision(slot_cur,
                                                &slot_cur->ctrl->revision))
                                return -1;
 
-               if (slot_cur->bus_on->current_speed == 0xFF) 
-                       if (get_cur_bus_info(&slot_cur)) 
+               if (slot_cur->bus_on->current_speed == 0xFF)
+                       if (get_cur_bus_info(&slot_cur))
                                return -1;
                get_max_bus_speed(slot_cur);
 
@@ -539,8 +539,8 @@ static int __init init_ops(void)
                debug("SLOT_PRESENT = %x\n", SLOT_PRESENT(slot_cur->status));
                debug("SLOT_LATCH = %x\n", SLOT_LATCH(slot_cur->status));
 
-               if ((SLOT_PWRGD(slot_cur->status)) && 
-                   !(SLOT_PRESENT(slot_cur->status)) && 
+               if ((SLOT_PWRGD(slot_cur->status)) &&
+                   !(SLOT_PRESENT(slot_cur->status)) &&
                    !(SLOT_LATCH(slot_cur->status))) {
                        debug("BEFORE POWER OFF COMMAND\n");
                                rc = power_off(slot_cur);
@@ -581,13 +581,13 @@ static int validate(struct slot *slot_cur, int opn)
 
        switch (opn) {
                case ENABLE:
-                       if (!(SLOT_PWRGD(slot_cur->status)) && 
-                            (SLOT_PRESENT(slot_cur->status)) && 
+                       if (!(SLOT_PWRGD(slot_cur->status)) &&
+                            (SLOT_PRESENT(slot_cur->status)) &&
                             !(SLOT_LATCH(slot_cur->status)))
                                return 0;
                        break;
                case DISABLE:
-                       if ((SLOT_PWRGD(slot_cur->status)) && 
+                       if ((SLOT_PWRGD(slot_cur->status)) &&
                            (SLOT_PRESENT(slot_cur->status)) &&
                            !(SLOT_LATCH(slot_cur->status)))
                                return 0;
@@ -617,7 +617,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
                err("out of system memory\n");
                return -ENOMEM;
        }
-        
+
        info->power_status = SLOT_PWRGD(slot_cur->status);
        info->attention_status = SLOT_ATTN(slot_cur->status,
                                                slot_cur->ext_status);
@@ -638,7 +638,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
                case BUS_SPEED_33:
                        break;
                case BUS_SPEED_66:
-                       if (mode == BUS_MODE_PCIX) 
+                       if (mode == BUS_MODE_PCIX)
                                bus_speed += 0x01;
                        else if (mode == BUS_MODE_PCI)
                                ;
@@ -654,8 +654,8 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
        }
 
        bus->cur_bus_speed = bus_speed;
-       // To do: bus_names 
-       
+       // To do: bus_names
+
        rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info);
        kfree(info);
        return rc;
@@ -729,8 +729,8 @@ static void ibm_unconfigure_device(struct pci_func *func)
 }
 
 /*
- * The following function is to fix kernel bug regarding 
- * getting bus entries, here we manually add those primary 
+ * The following function is to fix kernel bug regarding
+ * getting bus entries, here we manually add those primary
  * bus entries to kernel bus structure whenever apply
  */
 static u8 bus_structure_fixup(u8 busno)
@@ -814,7 +814,7 @@ static int ibm_configure_device(struct pci_func *func)
 }
 
 /*******************************************************
- * Returns whether the bus is empty or not 
+ * Returns whether the bus is empty or not
  *******************************************************/
 static int is_bus_empty(struct slot * slot_cur)
 {
@@ -842,7 +842,7 @@ static int is_bus_empty(struct slot * slot_cur)
 }
 
 /***********************************************************
- * If the HPC permits and the bus currently empty, tries to set the 
+ * If the HPC permits and the bus currently empty, tries to set the
  * bus speed and mode at the maximum card and bus capability
  * Parameters: slot
  * Returns: bus is set (0) or error code
@@ -856,7 +856,7 @@ static int set_bus(struct slot * slot_cur)
        static struct pci_device_id ciobx[] = {
                { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 0x0101) },
                { },
-       };      
+       };
 
        debug("%s - entry slot # %d\n", __func__, slot_cur->number);
        if (SET_BUS_STATUS(slot_cur->ctrl) && is_bus_empty(slot_cur)) {
@@ -877,7 +877,7 @@ static int set_bus(struct slot * slot_cur)
                                else if (!SLOT_BUS_MODE(slot_cur->ext_status))
                                        /* if max slot/bus capability is 66 pci
                                        and there's no bus mode mismatch, then
-                                       the adapter supports 66 pci */ 
+                                       the adapter supports 66 pci */
                                        cmd = HPC_BUS_66CONVMODE;
                                else
                                        cmd = HPC_BUS_33CONVMODE;
@@ -930,7 +930,7 @@ static int set_bus(struct slot * slot_cur)
                        return -EIO;
                }
        }
-       /* This is for x440, once Brandon fixes the firmware, 
+       /* This is for x440, once Brandon fixes the firmware,
        will not need this delay */
        msleep(1000);
        debug("%s -Exit\n", __func__);
@@ -938,9 +938,9 @@ static int set_bus(struct slot * slot_cur)
 }
 
 /* This routine checks the bus limitations that the slot is on from the BIOS.
- * This is used in deciding whether or not to power up the slot.  
+ * This is used in deciding whether or not to power up the slot.
  * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on
- * same bus) 
+ * same bus)
  * Parameters: slot
  * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus
  */
@@ -986,7 +986,7 @@ static int check_limitations(struct slot *slot_cur)
 static inline void print_card_capability(struct slot *slot_cur)
 {
        info("capability of the card is ");
-       if ((slot_cur->ext_status & CARD_INFO) == PCIX133) 
+       if ((slot_cur->ext_status & CARD_INFO) == PCIX133)
                info("   133 MHz PCI-X\n");
        else if ((slot_cur->ext_status & CARD_INFO) == PCIX66)
                info("    66 MHz PCI-X\n");
@@ -1020,7 +1020,7 @@ static int enable_slot(struct hotplug_slot *hs)
        }
 
        attn_LED_blink(slot_cur);
-       
+
        rc = set_bus(slot_cur);
        if (rc) {
                err("was not able to set the bus\n");
@@ -1082,7 +1082,7 @@ static int enable_slot(struct hotplug_slot *hs)
        rc = slot_update(&slot_cur);
        if (rc)
                goto error_power;
-       
+
        rc = -EINVAL;
        if (SLOT_POWER(slot_cur->status) && !(SLOT_PWRGD(slot_cur->status))) {
                err("power fault occurred trying to power up...\n");
@@ -1093,7 +1093,7 @@ static int enable_slot(struct hotplug_slot *hs)
                                        "speed and card capability\n");
                print_card_capability(slot_cur);
                goto error_power;
-       } 
+       }
        /* Don't think this case will happen after above checks...
         * but just in case, for paranoia sake */
        if (!(SLOT_POWER(slot_cur->status))) {
@@ -1144,7 +1144,7 @@ static int enable_slot(struct hotplug_slot *hs)
        ibmphp_print_test();
        rc = ibmphp_update_slot_info(slot_cur);
 exit:
-       ibmphp_unlock_operations(); 
+       ibmphp_unlock_operations();
        return rc;
 
 error_nopower:
@@ -1180,7 +1180,7 @@ static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
 {
        struct slot *slot = hotplug_slot->private;
        int rc;
-       
+
        ibmphp_lock_operations();
        rc = ibmphp_do_disable_slot(slot);
        ibmphp_unlock_operations();
@@ -1192,12 +1192,12 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
        int rc;
        u8 flag;
 
-       debug("DISABLING SLOT...\n"); 
-               
+       debug("DISABLING SLOT...\n");
+
        if ((slot_cur == NULL) || (slot_cur->ctrl == NULL)) {
                return -ENODEV;
        }
-       
+
        flag = slot_cur->flag;
        slot_cur->flag = 1;
 
@@ -1210,7 +1210,7 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
        attn_LED_blink(slot_cur);
 
        if (slot_cur->func == NULL) {
-               /* We need this for fncs's that were there on bootup */
+               /* We need this for functions that were there on bootup */
                slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
                if (!slot_cur->func) {
                        err("out of system memory\n");
@@ -1222,12 +1222,13 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
        }
 
        ibm_unconfigure_device(slot_cur->func);
-        
-       /* If we got here from latch suddenly opening on operating card or 
-       a power fault, there's no power to the card, so cannot
-       read from it to determine what resources it occupied.  This operation
-       is forbidden anyhow.  The best we can do is remove it from kernel
-       lists at least */
+
+       /*
+        * If we got here from latch suddenly opening on operating card or
+        * a power fault, there's no power to the card, so cannot
+        * read from it to determine what resources it occupied.  This operation
+        * is forbidden anyhow.  The best we can do is remove it from kernel
+        * lists at least */
 
        if (!flag) {
                attn_off(slot_cur);
@@ -1264,7 +1265,7 @@ error:
                rc = -EFAULT;
                goto exit;
        }
-       if (flag)               
+       if (flag)
                ibmphp_update_slot_info(slot_cur);
        goto exit;
 }
@@ -1339,7 +1340,7 @@ static int __init ibmphp_init(void)
        debug("AFTER Resource & EBDA INITIALIZATIONS\n");
 
        max_slots = get_max_slots();
-       
+
        if ((rc = ibmphp_register_pci()))
                goto error;
 
index 9df78bc14541526e0740eaa54123b3f9048bf872..bd044158b36c3ba963476ea1d32c139952de90cf 100644 (file)
@@ -123,7 +123,7 @@ static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void)
 static void __init print_bus_info (void)
 {
        struct bus_info *ptr;
-       
+
        list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
                debug ("%s - slot_min = %x\n", __func__, ptr->slot_min);
                debug ("%s - slot_max = %x\n", __func__, ptr->slot_max);
@@ -131,7 +131,7 @@ static void __init print_bus_info (void)
                debug ("%s - bus# = %x\n", __func__, ptr->busno);
                debug ("%s - current_speed = %x\n", __func__, ptr->current_speed);
                debug ("%s - controller_id = %x\n", __func__, ptr->controller_id);
-               
+
                debug ("%s - slots_at_33_conv = %x\n", __func__, ptr->slots_at_33_conv);
                debug ("%s - slots_at_66_conv = %x\n", __func__, ptr->slots_at_66_conv);
                debug ("%s - slots_at_66_pcix = %x\n", __func__, ptr->slots_at_66_pcix);
@@ -144,7 +144,7 @@ static void __init print_bus_info (void)
 static void print_lo_info (void)
 {
        struct rio_detail *ptr;
-       debug ("print_lo_info ----\n"); 
+       debug ("print_lo_info ----\n");
        list_for_each_entry(ptr, &rio_lo_head, rio_detail_list) {
                debug ("%s - rio_node_id = %x\n", __func__, ptr->rio_node_id);
                debug ("%s - rio_type = %x\n", __func__, ptr->rio_type);
@@ -176,7 +176,7 @@ static void __init print_ebda_pci_rsrc (void)
        struct ebda_pci_rsrc *ptr;
 
        list_for_each_entry(ptr, &ibmphp_ebda_pci_rsrc_head, ebda_pci_rsrc_list) {
-               debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", 
+               debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
                        __func__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
        }
 }
@@ -259,7 +259,7 @@ int __init ibmphp_access_ebda (void)
        ebda_seg = readw (io_mem);
        iounmap (io_mem);
        debug ("returned ebda segment: %x\n", ebda_seg);
-       
+
        io_mem = ioremap(ebda_seg<<4, 1);
        if (!io_mem)
                return -ENOMEM;
@@ -310,7 +310,7 @@ int __init ibmphp_access_ebda (void)
                        re = readw (io_mem + sub_addr); /* next sub blk */
 
                        sub_addr += 2;
-                       rc_id = readw (io_mem + sub_addr);      /* sub blk id */
+                       rc_id = readw (io_mem + sub_addr);      /* sub blk id */
 
                        sub_addr += 2;
                        if (rc_id != 0x5243)
@@ -330,7 +330,7 @@ int __init ibmphp_access_ebda (void)
                        debug ("info about hpc descriptor---\n");
                        debug ("hot blk format: %x\n", format);
                        debug ("num of controller: %x\n", num_ctlrs);
-                       debug ("offset of hpc data structure enteries: %x\n ", sub_addr);
+                       debug ("offset of hpc data structure entries: %x\n ", sub_addr);
 
                        sub_addr = base + re;   /* re sub blk */
                        /* FIXME: rc is never used/checked */
@@ -359,7 +359,7 @@ int __init ibmphp_access_ebda (void)
                        debug ("info about rsrc descriptor---\n");
                        debug ("format: %x\n", format);
                        debug ("num of rsrc: %x\n", num_entries);
-                       debug ("offset of rsrc data structure enteries: %x\n ", sub_addr);
+                       debug ("offset of rsrc data structure entries: %x\n ", sub_addr);
 
                        hs_complete = 1;
                } else {
@@ -376,7 +376,7 @@ int __init ibmphp_access_ebda (void)
                        rio_table_ptr->scal_count = readb (io_mem + offset + 1);
                        rio_table_ptr->riodev_count = readb (io_mem + offset + 2);
                        rio_table_ptr->offset = offset +3 ;
-                       
+
                        debug("info about rio table hdr ---\n");
                        debug("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ",
                                rio_table_ptr->ver_num, rio_table_ptr->scal_count,
@@ -440,12 +440,12 @@ static int __init ebda_rio_table (void)
                rio_detail_ptr->chassis_num = readb (io_mem + offset + 14);
 //             debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status);
                //create linked list of chassis
-               if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5) 
+               if (rio_detail_ptr->rio_type == 4 || rio_detail_ptr->rio_type == 5)
                        list_add (&rio_detail_ptr->rio_detail_list, &rio_vg_head);
-               //create linked list of expansion box                           
-               else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7) 
+               //create linked list of expansion box
+               else if (rio_detail_ptr->rio_type == 6 || rio_detail_ptr->rio_type == 7)
                        list_add (&rio_detail_ptr->rio_detail_list, &rio_lo_head);
-               else 
+               else
                        // not in my concern
                        kfree (rio_detail_ptr);
                offset += 15;
@@ -456,7 +456,7 @@ static int __init ebda_rio_table (void)
 }
 
 /*
- * reorganizing linked list of chassis  
+ * reorganizing linked list of chassis
  */
 static struct opt_rio *search_opt_vg (u8 chassis_num)
 {
@@ -464,7 +464,7 @@ static struct opt_rio *search_opt_vg (u8 chassis_num)
        list_for_each_entry(ptr, &opt_vg_head, opt_rio_list) {
                if (ptr->chassis_num == chassis_num)
                        return ptr;
-       }               
+       }
        return NULL;
 }
 
@@ -472,7 +472,7 @@ static int __init combine_wpg_for_chassis (void)
 {
        struct opt_rio *opt_rio_ptr = NULL;
        struct rio_detail *rio_detail_ptr = NULL;
-       
+
        list_for_each_entry(rio_detail_ptr, &rio_vg_head, rio_detail_list) {
                opt_rio_ptr = search_opt_vg (rio_detail_ptr->chassis_num);
                if (!opt_rio_ptr) {
@@ -484,14 +484,14 @@ static int __init combine_wpg_for_chassis (void)
                        opt_rio_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
                        opt_rio_ptr->middle_num = rio_detail_ptr->first_slot_num;
                        list_add (&opt_rio_ptr->opt_rio_list, &opt_vg_head);
-               } else {        
+               } else {
                        opt_rio_ptr->first_slot_num = min (opt_rio_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
                        opt_rio_ptr->middle_num = max (opt_rio_ptr->middle_num, rio_detail_ptr->first_slot_num);
-               }       
+               }
        }
        print_opt_vg ();
-       return 0;       
-}      
+       return 0;
+}
 
 /*
  * reorganizing linked list of expansion box
@@ -502,7 +502,7 @@ static struct opt_rio_lo *search_opt_lo (u8 chassis_num)
        list_for_each_entry(ptr, &opt_lo_head, opt_rio_lo_list) {
                if (ptr->chassis_num == chassis_num)
                        return ptr;
-       }               
+       }
        return NULL;
 }
 
@@ -510,7 +510,7 @@ static int combine_wpg_for_expansion (void)
 {
        struct opt_rio_lo *opt_rio_lo_ptr = NULL;
        struct rio_detail *rio_detail_ptr = NULL;
-       
+
        list_for_each_entry(rio_detail_ptr, &rio_lo_head, rio_detail_list) {
                opt_rio_lo_ptr = search_opt_lo (rio_detail_ptr->chassis_num);
                if (!opt_rio_lo_ptr) {
@@ -522,22 +522,22 @@ static int combine_wpg_for_expansion (void)
                        opt_rio_lo_ptr->first_slot_num = rio_detail_ptr->first_slot_num;
                        opt_rio_lo_ptr->middle_num = rio_detail_ptr->first_slot_num;
                        opt_rio_lo_ptr->pack_count = 1;
-                       
+
                        list_add (&opt_rio_lo_ptr->opt_rio_lo_list, &opt_lo_head);
-               } else {        
+               } else {
                        opt_rio_lo_ptr->first_slot_num = min (opt_rio_lo_ptr->first_slot_num, rio_detail_ptr->first_slot_num);
                        opt_rio_lo_ptr->middle_num = max (opt_rio_lo_ptr->middle_num, rio_detail_ptr->first_slot_num);
                        opt_rio_lo_ptr->pack_count = 2;
-               }       
+               }
        }
-       return 0;       
+       return 0;
 }
-       
+
 
 /* Since we don't know the max slot number per each chassis, hence go
  * through the list of all chassis to find out the range
- * Arguments: slot_num, 1st slot number of the chassis we think we are on, 
- * var (0 = chassis, 1 = expansion box) 
+ * Arguments: slot_num, 1st slot number of the chassis we think we are on,
+ * var (0 = chassis, 1 = expansion box)
  */
 static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
 {
@@ -547,7 +547,7 @@ static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
 
        if (!var) {
                list_for_each_entry(opt_vg_ptr, &opt_vg_head, opt_rio_list) {
-                       if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) { 
+                       if ((first_slot < opt_vg_ptr->first_slot_num) && (slot_num >= opt_vg_ptr->first_slot_num)) {
                                rc = -ENODEV;
                                break;
                        }
@@ -569,7 +569,7 @@ static struct opt_rio_lo * find_rxe_num (u8 slot_num)
 
        list_for_each_entry(opt_lo_ptr, &opt_lo_head, opt_rio_lo_list) {
                //check to see if this slot_num belongs to expansion box
-               if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1))) 
+               if ((slot_num >= opt_lo_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_lo_ptr->first_slot_num, 1)))
                        return opt_lo_ptr;
        }
        return NULL;
@@ -580,8 +580,8 @@ static struct opt_rio * find_chassis_num (u8 slot_num)
        struct opt_rio *opt_vg_ptr;
 
        list_for_each_entry(opt_vg_ptr, &opt_vg_head, opt_rio_list) {
-               //check to see if this slot_num belongs to chassis 
-               if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0))) 
+               //check to see if this slot_num belongs to chassis
+               if ((slot_num >= opt_vg_ptr->first_slot_num) && (!first_slot_num (slot_num, opt_vg_ptr->first_slot_num, 0)))
                        return opt_vg_ptr;
        }
        return NULL;
@@ -594,13 +594,13 @@ static u8 calculate_first_slot (u8 slot_num)
 {
        u8 first_slot = 1;
        struct slot * slot_cur;
-       
+
        list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
                if (slot_cur->ctrl) {
-                       if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num)) 
+                       if ((slot_cur->ctrl->ctlr_type != 4) && (slot_cur->ctrl->ending_slot_num > first_slot) && (slot_num > slot_cur->ctrl->ending_slot_num))
                                first_slot = slot_cur->ctrl->ending_slot_num;
                }
-       }                       
+       }
        return first_slot + 1;
 
 }
@@ -622,11 +622,11 @@ static char *create_file_name (struct slot * slot_cur)
                err ("Structure passed is empty\n");
                return NULL;
        }
-       
+
        slot_num = slot_cur->number;
 
        memset (str, 0, sizeof(str));
-       
+
        if (rio_table_ptr) {
                if (rio_table_ptr->ver_num == 3) {
                        opt_vg_ptr = find_chassis_num (slot_num);
@@ -660,7 +660,7 @@ static char *create_file_name (struct slot * slot_cur)
                        /* if both NULL and we DO have correct RIO table in BIOS */
                        return NULL;
                }
-       } 
+       }
        if (!flag) {
                if (slot_cur->ctrl->ctlr_type == 4) {
                        first_slot = calculate_first_slot (slot_num);
@@ -798,7 +798,7 @@ static int __init ebda_rsrc_controller (void)
                        slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num);
                        slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num);
 
-                       // create bus_info lined list --- if only one slot per bus: slot_min = slot_max 
+                       // create bus_info lined list --- if only one slot per bus: slot_min = slot_max
 
                        bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num);
                        if (!bus_info_ptr2) {
@@ -814,9 +814,9 @@ static int __init ebda_rsrc_controller (void)
                                bus_info_ptr1->index = bus_index++;
                                bus_info_ptr1->current_speed = 0xff;
                                bus_info_ptr1->current_bus_mode = 0xff;
-                               
+
                                bus_info_ptr1->controller_id = hpc_ptr->ctlr_id;
-                               
+
                                list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head);
 
                        } else {
@@ -851,7 +851,7 @@ static int __init ebda_rsrc_controller (void)
                                bus_info_ptr2->slots_at_66_conv = bus_ptr->slots_at_66_conv;
                                bus_info_ptr2->slots_at_66_pcix = bus_ptr->slots_at_66_pcix;
                                bus_info_ptr2->slots_at_100_pcix = bus_ptr->slots_at_100_pcix;
-                               bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix; 
+                               bus_info_ptr2->slots_at_133_pcix = bus_ptr->slots_at_133_pcix;
                        }
                        bus_ptr++;
                }
@@ -864,7 +864,7 @@ static int __init ebda_rsrc_controller (void)
                                hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1);
                                hpc_ptr->irq = readb (io_mem + addr + 2);
                                addr += 3;
-                               debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n", 
+                               debug ("ctrl bus = %x, ctlr devfun = %x, irq = %x\n",
                                        hpc_ptr->u.pci_ctlr.bus,
                                        hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq);
                                break;
@@ -932,7 +932,7 @@ static int __init ebda_rsrc_controller (void)
                                tmp_slot->supported_speed =  2;
                        else if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX)
                                tmp_slot->supported_speed =  1;
-                               
+
                        if ((hpc_ptr->slots[index].slot_cap & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP)
                                tmp_slot->supported_bus_mode = 1;
                        else
@@ -1000,7 +1000,7 @@ error_no_hpc:
        return rc;
 }
 
-/* 
+/*
  * map info (bus, devfun, start addr, end addr..) of i/o, memory,
  * pfm from the physical addr to a list of resource.
  */
@@ -1057,7 +1057,7 @@ static int __init ebda_rsrc_rsrc (void)
                        addr += 10;
 
                        debug ("rsrc from mem or pfm ---\n");
-                       debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n", 
+                       debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %x end addr: %x\n",
                                rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
 
                        list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
@@ -1096,7 +1096,7 @@ struct bus_info *ibmphp_find_same_bus_num (u32 num)
        struct bus_info *ptr;
 
        list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
-               if (ptr->busno == num) 
+               if (ptr->busno == num)
                         return ptr;
        }
        return NULL;
@@ -1110,7 +1110,7 @@ int ibmphp_get_bus_index (u8 num)
        struct bus_info *ptr;
 
        list_for_each_entry(ptr, &bus_info_head, bus_info_list) {
-               if (ptr->busno == num)  
+               if (ptr->busno == num)
                        return ptr->index;
        }
        return -ENODEV;
@@ -1168,7 +1168,7 @@ static struct pci_device_id id_table[] = {
                .subdevice      = HPC_SUBSYSTEM_ID,
                .class          = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
        }, {}
-};             
+};
 
 MODULE_DEVICE_TABLE(pci, id_table);
 
@@ -1197,7 +1197,7 @@ static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids)
        struct controller *ctrl;
 
        debug ("inside ibmphp_probe\n");
-       
+
        list_for_each_entry(ctrl, &ebda_hpc_head, ebda_hpc_list) {
                if (ctrl->ctlr_type == 1) {
                        if ((dev->devfn == ctrl->u.pci_ctlr.dev_fun) && (dev->bus->number == ctrl->u.pci_ctlr.bus)) {
@@ -1210,4 +1210,3 @@ static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids)
        }
        return -ENODEV;
 }
-
index f59ed30512b5f08d1a61e59910e802d2399cf850..5fc7a089f5323ce629175bdb568da1584d84d19a 100644 (file)
@@ -258,7 +258,7 @@ static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
 {
        u8 rc;
        void __iomem *wpg_addr; // base addr + offset
-       unsigned long wpg_data; // data to/from WPG LOHI format 
+       unsigned long wpg_data; // data to/from WPG LOHI format
        unsigned long ultemp;
        unsigned long data;     // actual data HILO format
        int i;
@@ -351,7 +351,7 @@ static u8 i2c_ctrl_write (struct controller *ctlr_ptr, void __iomem *WPGBbar, u8
 }
 
 //------------------------------------------------------------
-//  Read from ISA type HPC 
+//  Read from ISA type HPC
 //------------------------------------------------------------
 static u8 isa_ctrl_read (struct controller *ctlr_ptr, u8 offset)
 {
@@ -372,7 +372,7 @@ static void isa_ctrl_write (struct controller *ctlr_ptr, u8 offset, u8 data)
 {
        u16 start_address;
        u16 port_address;
-       
+
        start_address = ctlr_ptr->u.isa_ctlr.io_start;
        port_address = start_address + (u16) offset;
        outb (data, port_address);
@@ -656,11 +656,11 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
        //--------------------------------------------------------------------
        // cleanup
        //--------------------------------------------------------------------
-       
+
        // remove physical to logical address mapping
        if ((ctlr_ptr->ctlr_type == 2) || (ctlr_ptr->ctlr_type == 4))
                iounmap (wpg_bbar);
-       
+
        free_hpc_access ();
 
        debug_polling ("%s - Exit rc[%d]\n", __func__, rc);
@@ -835,7 +835,7 @@ static int poll_hpc(void *data)
                down (&semOperations);
 
                switch (poll_state) {
-               case POLL_LATCH_REGISTER: 
+               case POLL_LATCH_REGISTER:
                        oldlatchlow = curlatchlow;
                        ctrl_count = 0x00;
                        list_for_each (pslotlist, &ibmphp_slot_head) {
@@ -892,16 +892,16 @@ static int poll_hpc(void *data)
 
                        if (kthread_should_stop())
                                goto out_sleep;
-                       
+
                        down (&semOperations);
-                       
+
                        if (poll_count >= POLL_LATCH_CNT) {
                                poll_count = 0;
                                poll_state = POLL_SLOTS;
                        } else
                                poll_state = POLL_LATCH_REGISTER;
                        break;
-               }       
+               }
                /* give up the hardware semaphore */
                up (&semOperations);
                /* sleep for a short time just for good measure */
@@ -958,7 +958,7 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
        // bit 5 - HPC_SLOT_PWRGD
        if ((pslot->status & 0x20) != (poldslot->status & 0x20))
                // OFF -> ON: ignore, ON -> OFF: disable slot
-               if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) 
+               if ((poldslot->status & 0x20) && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status)))
                        disable = 1;
 
        // bit 6 - HPC_SLOT_BUS_SPEED
@@ -980,7 +980,7 @@ static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
                                        pslot->status &= ~HPC_SLOT_POWER;
                        }
                }
-               // CLOSE -> OPEN 
+               // CLOSE -> OPEN
                else if ((SLOT_PWRGD (poldslot->status) == HPC_SLOT_PWRGD_GOOD)
                        && (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED) && (SLOT_PRESENT (poldslot->status))) {
                        disable = 1;
@@ -1075,7 +1075,7 @@ void __exit ibmphp_hpc_stop_poll_thread (void)
        debug ("before locking operations \n");
        ibmphp_lock_operations ();
        debug ("after locking operations \n");
-       
+
        // wait for poll thread to exit
        debug ("before sem_exit down \n");
        down (&sem_exit);
index c60f5f3e838d4c07ec3e56b80db8bbbfd40d40ee..639ea3a75e1452ed825e7a2650fe635c2785f892 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * IBM Hot Plug Controller Driver
- * 
+ *
  * Written By: Irene Zubarev, IBM Corporation
- * 
+ *
  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
  * Copyright (C) 2001,2002 IBM Corp.
  *
@@ -42,7 +42,7 @@ static u8 find_sec_number (u8 primary_busno, u8 slotno);
 
 /*
  * NOTE..... If BIOS doesn't provide default routing, we assign:
- * 9 for SCSI, 10 for LAN adapters, and 11 for everything else. 
+ * 9 for SCSI, 10 for LAN adapters, and 11 for everything else.
  * If adapter is bridged, then we assign 11 to it and devices behind it.
  * We also assign the same irq numbers for multi function devices.
  * These are PIC mode, so shouldn't matter n.e.ways (hopefully)
@@ -71,11 +71,11 @@ static void assign_alt_irq (struct pci_func * cur_func, u8 class_code)
  * Configures the device to be added (will allocate needed resources if it
  * can), the device can be a bridge or a regular pci device, can also be
  * multi-functional
- * 
+ *
  * Input: function to be added
- * 
+ *
  * TO DO:  The error case with Multifunction device or multi function bridge,
- * if there is an error, will need to go through all previous functions and 
+ * if there is an error, will need to go through all previous functions and
  * unconfigure....or can add some code into unconfigure_card....
  */
 int ibmphp_configure_card (struct pci_func *func, u8 slotno)
@@ -98,7 +98,7 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
        cur_func = func;
 
        /* We only get bus and device from IRQ routing table.  So at this point,
-        * func->busno is correct, and func->device contains only device (at the 5 
+        * func->busno is correct, and func->device contains only device (at the 5
         * highest bits)
         */
 
@@ -151,7 +151,7 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
                                                     cur_func->device, cur_func->busno);
                                                cleanup_count = 6;
                                                goto error;
-                                       }       
+                                       }
                                        cur_func->next = NULL;
                                        function = 0x8;
                                        break;
@@ -339,7 +339,7 @@ error:
 }
 
 /*
- * This function configures the pci BARs of a single device.  
+ * This function configures the pci BARs of a single device.
  * Input: pointer to the pci_func
  * Output: configured PCI, 0, or error
  */
@@ -371,17 +371,17 @@ static int configure_device (struct pci_func *func)
 
        for (count = 0; address[count]; count++) {      /* for 6 BARs */
 
-               /* not sure if i need this.  per scott, said maybe need smth like this
+               /* not sure if i need this.  per scott, said maybe need * something like this
                   if devices don't adhere 100% to the spec, so don't want to write
                   to the reserved bits
 
-               pcibios_read_config_byte(cur_func->busno, cur_func->device, 
+               pcibios_read_config_byte(cur_func->busno, cur_func->device,
                PCI_BASE_ADDRESS_0 + 4 * count, &tmp);
                if (tmp & 0x01) // IO
-                       pcibios_write_config_dword(cur_func->busno, cur_func->device, 
+                       pcibios_write_config_dword(cur_func->busno, cur_func->device,
                        PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD);
                else  // Memory
-                       pcibios_write_config_dword(cur_func->busno, cur_func->device, 
+                       pcibios_write_config_dword(cur_func->busno, cur_func->device,
                        PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF);
                 */
                pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
@@ -421,8 +421,8 @@ static int configure_device (struct pci_func *func)
                                return -EIO;
                        }
                        pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->io[count]->start);
-       
-                       /* _______________This is for debugging purposes only_____________________ */ 
+
+                       /* _______________This is for debugging purposes only_____________________ */
                        debug ("b4 writing, the IO address is %x\n", func->io[count]->start);
                        pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
                        debug ("after writing.... the start address is %x\n", bar[count]);
@@ -484,7 +484,7 @@ static int configure_device (struct pci_func *func)
 
                                pci_bus_write_config_dword (ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start);
 
-                               /*_______________This is for debugging purposes only______________________________*/                            
+                               /*_______________This is for debugging purposes only______________________________*/
                                debug ("b4 writing, start address is %x\n", func->pfmem[count]->start);
                                pci_bus_read_config_dword (ibmphp_pci_bus, devfn, address[count], &bar[count]);
                                debug ("after writing, start address is %x\n", bar[count]);
@@ -559,7 +559,7 @@ static int configure_device (struct pci_func *func)
 /******************************************************************************
  * This routine configures a PCI-2-PCI bridge and the functions behind it
  * Parameters: pci_func
- * Returns: 
+ * Returns:
  ******************************************************************************/
 static int configure_bridge (struct pci_func **func_passed, u8 slotno)
 {
@@ -622,7 +622,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
        debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno);
 
        pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number);
-       
+
        /* __________________For debugging purposes only __________________________________
        pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
        debug ("sec_number after write/read is %x\n", sec_number);
@@ -644,7 +644,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
 
 
        /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-          !!!!!!!!!!!!!!!NEED TO ADD!!!  FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!! 
+          !!!!!!!!!!!!!!!NEED TO ADD!!!  FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!!
           !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
 
 
@@ -670,7 +670,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
                        debug ("len[count] in IO = %x\n", len[count]);
 
                        bus_io[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
-               
+
                        if (!bus_io[count]) {
                                err ("out of system memory\n");
                                retval = -ENOMEM;
@@ -735,7 +735,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
                                                ibmphp_add_pfmem_from_mem (bus_pfmem[count]);
                                                func->pfmem[count] = bus_pfmem[count];
                                        } else {
-                                               err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n", 
+                                               err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
                                                     func->busno, func->device, len[count]);
                                                kfree (mem_tmp);
                                                kfree (bus_pfmem[count]);
@@ -805,7 +805,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
        debug ("amount_needed->mem = %x\n", amount_needed->mem);
        debug ("amount_needed->pfmem =  %x\n", amount_needed->pfmem);
 
-       if (amount_needed->not_correct) {               
+       if (amount_needed->not_correct) {
                debug ("amount_needed is not correct\n");
                for (count = 0; address[count]; count++) {
                        /* for 2 BARs */
@@ -830,7 +830,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
        } else {
                debug ("it wants %x IO behind the bridge\n", amount_needed->io);
                io = kzalloc(sizeof(*io), GFP_KERNEL);
-               
+
                if (!io) {
                        err ("out of system memory\n");
                        retval = -ENOMEM;
@@ -959,7 +959,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
 
                if (bus->noIORanges) {
                        pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
-                       pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8); 
+                       pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);
 
                        /* _______________This is for debugging purposes only ____________________
                        pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp);
@@ -980,7 +980,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
                if (bus->noMemRanges) {
                        pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
                        pci_bus_write_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);
-                       
+
                        /* ____________________This is for debugging purposes only ________________________
                        pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp);
                        debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
@@ -1017,7 +1017,7 @@ static int configure_bridge (struct pci_func **func_passed, u8 slotno)
                pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
                if ((irq > 0x00) && (irq < 0x05))
                        pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
-               /*    
+               /*
                pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl);
                pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
                pci_bus_write_config_byte (ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
@@ -1071,7 +1071,7 @@ error:
  * This function adds up the amount of resources needed behind the PPB bridge
  * and passes it to the configure_bridge function
  * Input: bridge function
- * Ouput: amount of resources needed
+ * Output: amount of resources needed
  *****************************************************************************/
 static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
 {
@@ -1204,9 +1204,9 @@ static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
        return amount;
 }
 
-/* The following 3 unconfigure_boot_ routines deal with the case when we had the card 
- * upon bootup in the system, since we don't allocate func to such case, we need to read 
- * the start addresses from pci config space and then find the corresponding entries in 
+/* The following 3 unconfigure_boot_ routines deal with the case when we had the card
+ * upon bootup in the system, since we don't allocate func to such case, we need to read
+ * the start addresses from pci config space and then find the corresponding entries in
  * our resource lists.  The functions return either 0, -ENODEV, or -1 (general failure)
  * Change: we also call these functions even if we configured the card ourselves (i.e., not
  * the bootup case), since it should work same way
@@ -1561,8 +1561,8 @@ static int unconfigure_boot_card (struct slot *slot_cur)
  * unconfiguring the device
  * TO DO:  will probably need to add some code in case there was some resource,
  * to remove it... this is from when we have errors in the configure_card...
- *                     !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
- * Returns: 0, -1, -ENODEV 
+ *                     !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
+ * Returns: 0, -1, -ENODEV
  */
 int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
 {
@@ -1634,7 +1634,7 @@ int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
  * Input: bus and the amount of resources needed (we know we can assign those,
  *        since they've been checked already
  * Output: bus added to the correct spot
- *         0, -1, error 
+ *         0, -1, error
  */
 static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno)
 {
@@ -1650,7 +1650,7 @@ static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct r
                        err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n");
                        return -ENODEV;
                }
-       
+
                list_add (&bus->bus_list, &cur_bus->bus_list);
        }
        if (io) {
@@ -1679,7 +1679,7 @@ static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct r
        }
        if (pfmem) {
                pfmem_range = kzalloc(sizeof(*pfmem_range), GFP_KERNEL);
-               if (!pfmem_range) {     
+               if (!pfmem_range) {
                        err ("out of system memory\n");
                        return -ENOMEM;
                }
@@ -1726,4 +1726,3 @@ static u8 find_sec_number (u8 primary_busno, u8 slotno)
                return busno;
        return 0xff;
 }
-
index e2dc289f767ccfb4ac3096f6148bc1db3ee61d8f..a265acb2d5186502d7517c3405797b3882d305c7 100644 (file)
@@ -72,7 +72,7 @@ static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8
 static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr)
 {
        struct resource_node *rs;
-       
+
        if (!curr) {
                err ("NULL passed to allocate\n");
                return NULL;
@@ -128,7 +128,7 @@ static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node
        }
        newrange->start = curr->start_addr;
        newrange->end = curr->end_addr;
-               
+
        if (first_bus || (!num_ranges))
                newrange->rangeno = 1;
        else {
@@ -162,7 +162,7 @@ static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node
                        newbus->rangePFMem = newrange;
                        if (first_bus)
                                newbus->noPFMemRanges = 1;
-                       else {  
+                       else {
                                debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
                                ++newbus->noPFMemRanges;
                                fix_resources (newbus);
@@ -190,7 +190,7 @@ static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node
  * This is the Resource Management initialization function.  It will go through
  * the Resource list taken from EBDA and fill in this module's data structures
  *
- * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES, 
+ * THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
  * SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
  *
  * Input: ptr to the head of the resource list from EBDA
@@ -382,7 +382,7 @@ int __init ibmphp_rsrc_init (void)
  * pci devices' resources for the appropriate resource
  *
  * Input: type of the resource, range to add, current bus
- * Output: 0 or -1, bus and range ptrs 
+ * Output: 0 or -1, bus and range ptrs
  ********************************************************************************/
 static int add_bus_range (int type, struct range_node *range, struct bus_node *bus_cur)
 {
@@ -466,7 +466,7 @@ static void update_resources (struct bus_node *bus_cur, int type, int rangeno)
 
        switch (type) {
                case MEM:
-                       if (bus_cur->firstMem) 
+                       if (bus_cur->firstMem)
                                res = bus_cur->firstMem;
                        break;
                case PFMEM:
@@ -583,7 +583,7 @@ static void fix_resources (struct bus_node *bus_cur)
 }
 
 /*******************************************************************************
- * This routine adds a resource to the list of resources to the appropriate bus 
+ * This routine adds a resource to the list of resources to the appropriate bus
  * based on their resource type and sorted by their starting addresses.  It assigns
  * the ptrs to next and nextRange if needed.
  *
@@ -605,11 +605,11 @@ int ibmphp_add_resource (struct resource_node *res)
                err ("NULL passed to add\n");
                return -ENODEV;
        }
-       
+
        bus_cur = find_bus_wprev (res->busno, NULL, 0);
-       
+
        if (!bus_cur) {
-               /* didn't find a bus, smth's wrong!!! */
+               /* didn't find a bus, something's wrong!!! */
                debug ("no bus in the system, either pci_dev's wrong or allocation failed\n");
                return -ENODEV;
        }
@@ -648,7 +648,7 @@ int ibmphp_add_resource (struct resource_node *res)
        if (!range_cur) {
                switch (res->type) {
                        case IO:
-                               ++bus_cur->needIOUpdate;                                        
+                               ++bus_cur->needIOUpdate;
                                break;
                        case MEM:
                                ++bus_cur->needMemUpdate;
@@ -659,13 +659,13 @@ int ibmphp_add_resource (struct resource_node *res)
                }
                res->rangeno = -1;
        }
-       
+
        debug ("The range is %d\n", res->rangeno);
        if (!res_start) {
                /* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
                switch (res->type) {
                        case IO:
-                               bus_cur->firstIO = res;                                 
+                               bus_cur->firstIO = res;
                                break;
                        case MEM:
                                bus_cur->firstMem = res;
@@ -673,7 +673,7 @@ int ibmphp_add_resource (struct resource_node *res)
                        case PFMEM:
                                bus_cur->firstPFMem = res;
                                break;
-               }       
+               }
                res->next = NULL;
                res->nextRange = NULL;
        } else {
@@ -770,7 +770,7 @@ int ibmphp_add_resource (struct resource_node *res)
  * This routine will remove the resource from the list of resources
  *
  * Input: io, mem, and/or pfmem resource to be deleted
- * Ouput: modified resource list
+ * Output: modified resource list
  *        0 or error code
  ****************************************************************************/
 int ibmphp_remove_resource (struct resource_node *res)
@@ -825,7 +825,7 @@ int ibmphp_remove_resource (struct resource_node *res)
 
        if (!res_cur) {
                if (res->type == PFMEM) {
-                       /* 
+                       /*
                         * case where pfmem might be in the PFMemFromMem list
                         * so will also need to remove the corresponding mem
                         * entry
@@ -961,12 +961,12 @@ static struct range_node * find_range (struct bus_node *bus_cur, struct resource
 }
 
 /*****************************************************************************
- * This routine will check to make sure the io/mem/pfmem->len that the device asked for 
+ * This routine will check to make sure the io/mem/pfmem->len that the device asked for
  * can fit w/i our list of available IO/MEM/PFMEM resources.  If cannot, returns -EINVAL,
  * otherwise, returns 0
  *
  * Input: resource
- * Ouput: the correct start and end address are inputted into the resource node,
+ * Output: the correct start and end address are inputted into the resource node,
  *        0 or -EINVAL
  *****************************************************************************/
 int ibmphp_check_resource (struct resource_node *res, u8 bridge)
@@ -996,7 +996,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
        bus_cur = find_bus_wprev (res->busno, NULL, 0);
 
        if (!bus_cur) {
-               /* didn't find a bus, smth's wrong!!! */
+               /* didn't find a bus, something's wrong!!! */
                debug ("no bus in the system, either pci_dev's wrong or allocation failed\n");
                return -EINVAL;
        }
@@ -1066,7 +1066,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                                                                break;
                                                }
                                        }
-                       
+
                                        if (flag && len_cur == res->len) {
                                                debug ("but we are not here, right?\n");
                                                res->start = start_cur;
@@ -1118,10 +1118,10 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
                if (res_prev) {
                        if (res_prev->rangeno != res_cur->rangeno) {
                                /* 1st device on this range */
-                               if ((res_cur->start != range->start) && 
+                               if ((res_cur->start != range->start) &&
                                        ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
                                        if ((len_tmp < len_cur) || (len_cur == 0)) {
-                                               if ((range->start % tmp_divide) == 0) { 
+                                               if ((range->start % tmp_divide) == 0) {
                                                        /* just perfect, starting address is divisible by length */
                                                        flag = 1;
                                                        len_cur = len_tmp;
@@ -1344,7 +1344,7 @@ int ibmphp_check_resource (struct resource_node *res, u8 bridge)
  * This routine is called from remove_card if the card contained PPB.
  * It will remove all the resources on the bus as well as the bus itself
  * Input: Bus
- * Ouput: 0, -ENODEV
+ * Output: 0, -ENODEV
  ********************************************************************************/
 int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
 {
@@ -1353,7 +1353,7 @@ int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
        struct bus_node *prev_bus;
        int rc;
 
-       prev_bus = find_bus_wprev (parent_busno, NULL, 0);      
+       prev_bus = find_bus_wprev (parent_busno, NULL, 0);
 
        if (!prev_bus) {
                debug ("something terribly wrong. Cannot find parent bus to the one to remove\n");
@@ -1424,7 +1424,7 @@ int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
 }
 
 /******************************************************************************
- * This routine deletes the ranges from a given bus, and the entries from the 
+ * This routine deletes the ranges from a given bus, and the entries from the
  * parent's bus in the resources
  * Input: current bus, previous bus
  * Output: 0, -EINVAL
@@ -1453,7 +1453,7 @@ static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
        if (bus_cur->noMemRanges) {
                range_cur = bus_cur->rangeMem;
                for (i = 0; i < bus_cur->noMemRanges; i++) {
-                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0) 
+                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0)
                                return -EINVAL;
 
                        ibmphp_remove_resource (res);
@@ -1467,7 +1467,7 @@ static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
        if (bus_cur->noPFMemRanges) {
                range_cur = bus_cur->rangePFMem;
                for (i = 0; i < bus_cur->noPFMemRanges; i++) {
-                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0) 
+                       if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0)
                                return -EINVAL;
 
                        ibmphp_remove_resource (res);
@@ -1482,7 +1482,7 @@ static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
 }
 
 /*
- * find the resource node in the bus 
+ * find the resource node in the bus
  * Input: Resource needed, start address of the resource, type of resource
  */
 int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
@@ -1512,7 +1512,7 @@ int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resour
                        err ("wrong type of flag\n");
                        return -EINVAL;
        }
-       
+
        while (res_cur) {
                if (res_cur->start == start_address) {
                        *res = res_cur;
@@ -1718,7 +1718,7 @@ static int __init once_over (void)
                        }       /* end for pfmem */
                }       /* end if */
        }       /* end list_for_each bus */
-       return 0; 
+       return 0;
 }
 
 int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem)
@@ -1760,9 +1760,9 @@ static struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u
        list_for_each (tmp, &gbuses) {
                tmp_prev = tmp->prev;
                bus_cur = list_entry (tmp, struct bus_node, bus_list);
-               if (flag) 
+               if (flag)
                        *prev = list_entry (tmp_prev, struct bus_node, bus_list);
-               if (bus_cur->busno == bus_number) 
+               if (bus_cur->busno == bus_number)
                        return bus_cur;
        }
 
@@ -1776,7 +1776,7 @@ void ibmphp_print_test (void)
        struct range_node *range;
        struct resource_node *res;
        struct list_head *tmp;
-       
+
        debug_pci ("*****************START**********************\n");
 
        if ((!list_empty(&gbuses)) && flags) {
@@ -1906,7 +1906,7 @@ static int range_exists_already (struct range_node * range, struct bus_node * bu
                        return 1;
                range_cur = range_cur->next;
        }
-       
+
        return 0;
 }
 
@@ -1920,7 +1920,7 @@ static int range_exists_already (struct range_node * range, struct bus_node * bu
  * Returns: none
  * Note: this function doesn't take into account IO restrictions etc,
  *      so will only work for bridges with no video/ISA devices behind them It
- *      also will not work for onboard PPB's that can have more than 1 *bus
+ *      also will not work for onboard PPBs that can have more than 1 *bus
  *      behind them All these are TO DO.
  *      Also need to add more error checkings... (from fnc returns etc)
  */
@@ -1963,7 +1963,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
                                        case PCI_HEADER_TYPE_BRIDGE:
                                                function = 0x8;
                                        case PCI_HEADER_TYPE_MULTIBRIDGE:
-                                               /* We assume here that only 1 bus behind the bridge 
+                                               /* We assume here that only 1 bus behind the bridge
                                                   TO DO: add functionality for several:
                                                   temp = secondary;
                                                   while (temp < subordinate) {
@@ -1972,7 +1972,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
                                                   }
                                                 */
                                                pci_bus_read_config_byte (ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
-                                               bus_sec = find_bus_wprev (sec_busno, NULL, 0); 
+                                               bus_sec = find_bus_wprev (sec_busno, NULL, 0);
                                                /* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
                                                if (!bus_sec) {
                                                        bus_sec = alloc_error_bus (NULL, sec_busno, 1);
@@ -2028,7 +2028,7 @@ static int __init update_bridge_ranges (struct bus_node **bus)
                                                                io->len = io->end - io->start + 1;
                                                                ibmphp_add_resource (io);
                                                        }
-                                               }       
+                                               }
 
                                                pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
                                                pci_bus_read_config_word (ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address);
index ec20f74c8981ce1a4790f0d3102759b4c68af8ef..cfa92a984e622ff0c8719488191daf0f618d8297 100644 (file)
@@ -131,7 +131,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
        }
        module_put(slot->ops->owner);
 
-exit:  
+exit:
        if (retval)
                return retval;
        return count;
@@ -177,7 +177,7 @@ static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
                retval = ops->set_attention_status(slot->hotplug, attention);
        module_put(ops->owner);
 
-exit:  
+exit:
        if (retval)
                return retval;
        return count;
@@ -247,7 +247,7 @@ static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
                retval = slot->ops->hardware_test(slot, test);
        module_put(slot->ops->owner);
 
-exit:  
+exit:
        if (retval)
                return retval;
        return count;
@@ -512,7 +512,7 @@ int pci_hp_deregister(struct hotplug_slot *hotplug)
  * @hotplug: pointer to the slot whose info has changed
  * @info: pointer to the info copy into the slot's info structure
  *
- * @slot must have been registered with the pci 
+ * @slot must have been registered with the pci
  * hotplug subsystem previously with a call to pci_hp_register().
  *
  * Returns 0 if successful, anything else for an error.
index 541bbe6d5343e9c99da97f9e5fd8c74c554b24d3..21e865ded1dce01fde77bb29312e2a653be6b4fa 100644 (file)
@@ -180,5 +180,5 @@ static inline int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
 {
        return 0;
 }
-#endif                                 /* CONFIG_ACPI */
+#endif                         /* CONFIG_ACPI */
 #endif                         /* _PCIEHP_H */
index ead7c534095e885572c85c6963d57742dcab7bec..eddddd447d0df61074d7a133524f37879ab73741 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;
 }
@@ -78,7 +78,7 @@ static int __initdata dup_slot_id;
 static int __initdata acpi_slot_detected;
 static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
 
-/* Dummy driver for dumplicate name detection */
+/* Dummy driver for duplicate name detection */
 static int __init dummy_probe(struct pcie_device *dev)
 {
        u32 slot_cap;
@@ -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 f4a18f51a29cc3bc1c17121ab265ce9e199da0ec..bbd48bbe4e9be2fe9dacdd52388f0256bad25203 100644 (file)
@@ -351,8 +351,8 @@ static int __init pcied_init(void)
 
        pciehp_firmware_init();
        retval = pcie_port_service_register(&hpdriver_portdrv);
-       dbg("pcie_port_service_register = %d\n", retval);
-       info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+       dbg("pcie_port_service_register = %d\n", retval);
+       info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
        if (retval)
                dbg("Failure to register service\n");
 
index 51f56ef4ab6f83a9382527f9061172800fff589a..3eea3fdd4b0b78381c5c9604bac5556e514213b7 100644 (file)
@@ -92,7 +92,7 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)
 {
        /* Clamp to sane value */
        if ((sec <= 0) || (sec > 60))
-               sec = 2;
+               sec = 2;
 
        ctrl->poll_timer.function = &int_poll_timeout;
        ctrl->poll_timer.data = (unsigned long)ctrl;
@@ -194,7 +194,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
                        ctrl_dbg(ctrl, "CMD_COMPLETED not clear after 1 sec\n");
                } else if (!NO_CMD_CMPL(ctrl)) {
                        /*
-                        * This controller semms to notify of command completed
+                        * This controller seems to notify of command completed
                         * event even though it supports none of power
                         * controller, attention led, power led and EMI.
                         */
@@ -926,7 +926,7 @@ struct controller *pcie_init(struct pcie_device *dev)
        if (pciehp_writew(ctrl, PCI_EXP_SLTSTA, 0x1f))
                goto abort_ctrl;
 
-       /* Disable sotfware notification */
+       /* Disable software notification */
        pcie_disable_notification(ctrl);
 
        ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n",
index 1f00b937f721810676c3270f6bd8ffeccdefccc0..ac69094e4b2089804ce41b00730d81c487adebf7 100644 (file)
@@ -52,7 +52,7 @@ static LIST_HEAD(slot_list);
        do {                                                    \
                if (debug)                                      \
                        printk (KERN_DEBUG "%s: " format "\n",  \
-                               MY_NAME , ## arg);              \
+                               MY_NAME , ## arg);              \
        } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
@@ -287,7 +287,7 @@ static int __init init_slots(void)
                hotplug_slot->release = &release_slot;
                make_slot_name(slot);
                hotplug_slot->ops = &skel_hotplug_slot_ops;
-               
+
                /*
                 * Initialize the slot info structure with some known
                 * good values.
@@ -296,7 +296,7 @@ static int __init init_slots(void)
                get_attention_status(hotplug_slot, &info->attention_status);
                get_latch_status(hotplug_slot, &info->latch_status);
                get_adapter_status(hotplug_slot, &info->adapter_status);
-               
+
                dbg("registering slot %d\n", i);
                retval = pci_hp_register(slot->hotplug_slot);
                if (retval) {
@@ -336,7 +336,7 @@ static void __exit cleanup_slots(void)
                pci_hp_deregister(slot->hotplug_slot);
        }
 }
-               
+
 static int __init pcihp_skel_init(void)
 {
        int retval;
index bb7af78e4eedd359a08db0fab40682a82abac5e1..e9c044d15add0e9c06bfbe74f736312e0cf1128d 100644 (file)
@@ -217,7 +217,7 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
        if (!pcibios_find_pci_bus(dn))
                return -EINVAL;
 
-       /* If pci slot is hotplugable, use hotplug to remove it */
+       /* If pci slot is hotpluggable, use hotplug to remove it */
        slot = find_php_slot(dn);
        if (slot && rpaphp_deregister_slot(slot)) {
                printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
index 3135856e5e1cc62eda01bb0af2fe4b9407dcc445..b2593e876a097c6ee42d0044069e6ce4f8617738 100644 (file)
@@ -49,9 +49,9 @@
 extern bool rpaphp_debug;
 #define dbg(format, arg...)                                    \
        do {                                                    \
-               if (rpaphp_debug)                                       \
+               if (rpaphp_debug)                               \
                        printk(KERN_DEBUG "%s: " format,        \
-                               MY_NAME , ## arg);              \
+                               MY_NAME , ## arg);              \
        } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
 #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
@@ -99,5 +99,5 @@ void dealloc_slot_struct(struct slot *slot);
 struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
 int rpaphp_register_slot(struct slot *slot);
 int rpaphp_deregister_slot(struct slot *slot);
-       
+
 #endif                         /* _PPC64PHP_H */
index 127d6e6001858e4955cae3afa92689ee1ac593b6..b7fc5c9255a5b6fb76f25f54dd4fac5ec06bc331 100644 (file)
@@ -226,7 +226,7 @@ int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
        for (i = 0; i < indexes[0]; i++) {
                if ((unsigned int) indexes[i + 1] == *my_index) {
                        if (drc_name)
-                               *drc_name = name_tmp;
+                               *drc_name = name_tmp;
                        if (drc_type)
                                *drc_type = type_tmp;
                        if (drc_index)
@@ -289,7 +289,7 @@ static int is_php_dn(struct device_node *dn, const int **indexes,
  * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem.
  * @dn: device node of slot
  *
- * This subroutine will register a hotplugable slot with the
+ * This subroutine will register a hotpluggable slot with the
  * PCI hotplug infrastructure. This routine is typically called
  * during boot time, if the hotplug slots are present at boot time,
  * or is called later, by the dlpar add code, if the slot is
@@ -328,7 +328,7 @@ int rpaphp_add_slot(struct device_node *dn)
                        return -ENOMEM;
 
                slot->type = simple_strtoul(type, NULL, 10);
-                               
+
                dbg("Found drc-index:0x%x drc-name:%s drc-type:%s\n",
                                indexes[i + 1], name, type);
 
@@ -356,7 +356,7 @@ static void __exit cleanup_slots(void)
        /*
         * Unregister all of our slots with the pci_hotplug subsystem,
         * and free up all memory that we had allocated.
-        * memory will be freed in release_slot callback. 
+        * memory will be freed in release_slot callback.
         */
 
        list_for_each_safe(tmp, n, &rpaphp_slot_head) {
index 513e1e2823914dddf71b7ecc7f5d58ac13010459..9243f3e7a1c9c89eab8656ddde876f80f36cb0f5 100644 (file)
@@ -44,7 +44,7 @@ int rpaphp_get_sensor_state(struct slot *slot, int *state)
                        dbg("%s: slot must be power up to get sensor-state\n",
                            __func__);
 
-                       /* some slots have to be powered up 
+                       /* some slots have to be powered up
                         * before get-sensor will succeed.
                         */
                        rc = rtas_set_power_level(slot->power_domain, POWER_ON,
@@ -133,4 +133,3 @@ int rpaphp_enable_slot(struct slot *slot)
 
        return 0;
 }
-
index b283bbea6d24ad4132844694ae68351ff920e28f..a6082cc263f759e2d361b96c9d04e5540a72ce0d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * RPA Virtual I/O device functions 
+ * RPA Virtual I/O device functions
  * Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
  *
  * All rights reserved.
@@ -51,27 +51,27 @@ struct slot *alloc_slot_struct(struct device_node *dn,
                        int drc_index, char *drc_name, int power_domain)
 {
        struct slot *slot;
-       
+
        slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
        if (!slot)
                goto error_nomem;
        slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
        if (!slot->hotplug_slot)
-               goto error_slot;        
+               goto error_slot;
        slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
                                           GFP_KERNEL);
        if (!slot->hotplug_slot->info)
                goto error_hpslot;
        slot->name = kstrdup(drc_name, GFP_KERNEL);
        if (!slot->name)
-               goto error_info;        
+               goto error_info;
        slot->dn = dn;
        slot->index = drc_index;
        slot->power_domain = power_domain;
        slot->hotplug_slot->private = slot;
        slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
        slot->hotplug_slot->release = &rpaphp_release_slot;
-       
+
        return (slot);
 
 error_info:
@@ -91,7 +91,7 @@ static int is_registered(struct slot *slot)
        list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
                if (!strcmp(tmp_slot->name, slot->name))
                        return 1;
-       }       
+       }
        return 0;
 }
 
@@ -104,7 +104,7 @@ int rpaphp_deregister_slot(struct slot *slot)
                __func__, slot->name);
 
        list_del(&slot->rpaphp_slot_list);
-       
+
        retval = pci_hp_deregister(php_slot);
        if (retval)
                err("Problem unregistering a slot %s\n", slot->name);
@@ -120,7 +120,7 @@ int rpaphp_register_slot(struct slot *slot)
        int retval;
        int slotno;
 
-       dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n", 
+       dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
                __func__, slot->dn->full_name, slot->index, slot->name,
                slot->power_domain, slot->type);
 
@@ -128,7 +128,7 @@ int rpaphp_register_slot(struct slot *slot)
        if (is_registered(slot)) {
                err("rpaphp_register_slot: slot[%s] is already registered\n", slot->name);
                return -EAGAIN;
-       }       
+       }
 
        if (slot->dn->child)
                slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
@@ -145,4 +145,3 @@ int rpaphp_register_slot(struct slot *slot)
        info("Slot [%s] registered\n", slot->name);
        return 0;
 }
-
index b2781dfe60e9e3cbc79095de46b96f8421958275..5b05a68cca6c73aaf50c7f66ccec5923e4f3d431 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 d876e4b3c6a98d8412f154a4f63814ec5b27682c..61529097464d4402f19d22dd33b77433b16c5413 100644 (file)
@@ -216,13 +216,13 @@ struct ctrl_reg {
 
 /* offsets to the controller registers based on the above structure layout */
 enum ctrl_offsets {
-       BASE_OFFSET      = offsetof(struct ctrl_reg, base_offset),
-       SLOT_AVAIL1      = offsetof(struct ctrl_reg, slot_avail1),
+       BASE_OFFSET      = offsetof(struct ctrl_reg, base_offset),
+       SLOT_AVAIL1      = offsetof(struct ctrl_reg, slot_avail1),
        SLOT_AVAIL2      = offsetof(struct ctrl_reg, slot_avail2),
-       SLOT_CONFIG      = offsetof(struct ctrl_reg, slot_config),
+       SLOT_CONFIG      = offsetof(struct ctrl_reg, slot_config),
        SEC_BUS_CONFIG   = offsetof(struct ctrl_reg, sec_bus_config),
        MSI_CTRL         = offsetof(struct ctrl_reg, msi_ctrl),
-       PROG_INTERFACE   = offsetof(struct ctrl_reg, prog_interface),
+       PROG_INTERFACE   = offsetof(struct ctrl_reg, prog_interface),
        CMD              = offsetof(struct ctrl_reg, cmd),
        CMD_STATUS       = offsetof(struct ctrl_reg, cmd_status),
        INTR_LOC         = offsetof(struct ctrl_reg, intr_loc),
index d3f757df691c9c6bf46cbb26caca51ff59fb51e6..faf13abd5b99ded8d19fe2461c6807ac0e883ebf 100644 (file)
@@ -143,11 +143,11 @@ static int init_slots(struct controller *ctrl)
                snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
                hotplug_slot->ops = &shpchp_hotplug_slot_ops;
 
-               ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x "
-                        "hp_slot=%x sun=%x slot_device_offset=%x\n",
-                        pci_domain_nr(ctrl->pci_dev->subordinate),
-                        slot->bus, slot->device, slot->hp_slot, slot->number,
-                        ctrl->slot_device_offset);
+               ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x "
+                        "hp_slot=%x sun=%x slot_device_offset=%x\n",
+                        pci_domain_nr(ctrl->pci_dev->subordinate),
+                        slot->bus, slot->device, slot->hp_slot, slot->number,
+                        ctrl->slot_device_offset);
                retval = pci_hp_register(slot->hotplug_slot,
                                ctrl->pci_dev->subordinate, slot->device, name);
                if (retval) {
index 75ba2311b54f3f37b62176a37e032c480be9f3f1..2d7f474ca0ec7036d023dbc1b413375bb0fd37ae 100644 (file)
 #define SLOT_REG_RSVDZ_MASK    ((1 << 15) | (7 << 21))
 
 /*
- * SHPC Command Code definitnions
+ * SHPC Command Code definitions
  *
  *     Slot Operation                          00h - 3Fh
  *     Set Bus Segment Speed/Mode A            40h - 47h
index 1b90579b233ae8c2d7db5efe00cc022c42fbb73a..50ce6809829836c38d4aedfa58aeb642b126d9df 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 21a7182dccd435865347d223ead22e73a02fe0db..1fe2d6fb19d5046ae78aaa6df3f26f37c12bf6bf 100644 (file)
@@ -610,7 +610,7 @@ resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
        struct resource tmp;
        enum pci_bar_type type;
        int reg = pci_iov_resource_bar(dev, resno, &type);
-       
+
        if (!reg)
                return 0;
 
index b008cf86b9c3f8d4fab1493a41895f196519e94d..6684f153ab57f57abf1ba4f63832c947e04b628a 100644 (file)
@@ -25,7 +25,7 @@ static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason)
 /**
  * pci_lost_interrupt - reports a lost PCI interrupt
  * @pdev:      device whose interrupt is lost
- * 
+ *
  * The primary function of this routine is to report a lost interrupt
  * in a standard way which users can recognise (instead of blaming the
  * driver).
index 5e63645a7abe8fcf3d5880aa3cdbd001ce927109..3fcd67a16677afc4b73495347b38e2fc418b7a04 100644 (file)
@@ -784,7 +784,7 @@ error:
  * @nvec: how many MSIs have been requested ?
  * @type: are we checking for MSI or MSI-X ?
  *
- * Look at global flags, the device itself, and its parent busses
+ * Look at global flags, the device itself, and its parent buses
  * to determine if MSI/-X are supported for the device. If MSI/-X is
  * supported return 0, else return an error code.
  **/
index dfd1f59de729c6293416d789fb6f665dc09bf701..577074efbe62f93f39ac00c6f5c4963d53bd2b0d 100644 (file)
@@ -141,7 +141,7 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
  * if (_PRW at S-state x)
  *     choose from highest power _SxD to lowest power _SxW
  * else // no _PRW at S-state x
- *     choose highest power _SxD or any lower power
+ *     choose highest power _SxD or any lower power
  */
 
 static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
@@ -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 454853507b7ebc580212d0f0514cf8e0a1bb8e44..9042fdbd724405bc96808ed699a1d543fcf17c17 100644 (file)
@@ -312,7 +312,7 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
  * __pci_device_probe - check if a driver wants to claim a specific PCI device
  * @drv: driver to call to check if it wants the PCI device
  * @pci_dev: PCI device being probed
- * 
+ *
  * returns 0 on success, else error.
  * side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
  */
@@ -378,7 +378,7 @@ static int pci_device_remove(struct device * dev)
         * We would love to complain here if pci_dev->is_enabled is set, that
         * the driver should have called pci_disable_device(), but the
         * unfortunate fact is there are too many odd BIOS and bridge setups
-        * that don't like drivers doing that all of the time.  
+        * that don't like drivers doing that all of the time.
         * Oh well, we can dream of sane hardware when we sleep, no matter how
         * horrible the crap we have to deal with is when we are awake...
         */
@@ -1156,10 +1156,10 @@ static const struct dev_pm_ops pci_dev_pm_ops = {
  * @drv: the driver structure to register
  * @owner: owner module of drv
  * @mod_name: module name string
- * 
+ *
  * Adds the driver structure to the list of registered drivers.
- * Returns a negative value on error, otherwise 0. 
- * If no error occurred, the driver remains registered even if 
+ * Returns a negative value on error, otherwise 0.
+ * If no error occurred, the driver remains registered even if
  * no device was claimed during registration.
  */
 int __pci_register_driver(struct pci_driver *drv, struct module *owner,
@@ -1181,7 +1181,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
 /**
  * pci_unregister_driver - unregister a pci driver
  * @drv: the driver structure to unregister
- * 
+ *
  * Deletes the driver structure from the list of registered PCI drivers,
  * gives it a chance to clean up by calling its remove() function for
  * each device it was responsible for, and marks those devices as
@@ -1203,7 +1203,7 @@ static struct pci_driver pci_compat_driver = {
  * pci_dev_driver - get the pci_driver of a device
  * @dev: the device to query
  *
- * Returns the appropriate pci_driver structure or %NULL if there is no 
+ * Returns the appropriate pci_driver structure or %NULL if there is no
  * registered driver for the device.
  */
 struct pci_driver *
@@ -1224,7 +1224,7 @@ pci_dev_driver(const struct pci_dev *dev)
  * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
  * @dev: the PCI device structure to match against
  * @drv: the device driver to search for matching PCI device id structures
- * 
+ *
  * Used by a driver to check whether a PCI device present in the
  * system is in its list of supported devices. Returns the matching
  * pci_device_id structure or %NULL if there is no match.
index edaed6f4da6cebfbe1c0e2b1b40ac94a039af9dc..d51f45aa669e5ff9184daab1df72776e8165aa84 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 6e47c519c51070788ea7fa4e0a3f56896b962ab9..2ff77509d8e53b505dc3f73b35567920491b2f7d 100644 (file)
@@ -2,13 +2,13 @@
  *
  * Copyright (C) 2008 Red Hat, Inc.
  * Author:
- *     Chris Wright
+ *     Chris Wright
  *
  * This work is licensed under the terms of the GNU GPL, version 2.
  *
  * Usage is simple, allocate a new id to the stub driver and bind the
  * device to it.  For example:
- * 
+ *
  * # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id
  * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
  * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind
index 2aaa83c85a4edbfa9d587b05a79507292ea7c751..c91e6c18debcdd14c5151d74c1c66ccb9b049a41 100644 (file)
@@ -10,7 +10,7 @@
  *
  * File attributes for PCI devices
  *
- * Modeled after usb's driverfs.c 
+ * Modeled after usb's driverfs.c
  *
  */
 
@@ -270,13 +270,17 @@ msi_bus_store(struct device *dev, struct device_attribute *attr,
        if (kstrtoul(buf, 0, &val) < 0)
                return -EINVAL;
 
-       /* bad things may happen if the no_msi flag is changed
-        * while some drivers are loaded */
+       /*
+        * Bad things may happen if the no_msi flag is changed
+        * while drivers are loaded.
+        */
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       /* Maybe pci devices without subordinate busses shouldn't even have this
-        * attribute in the first place?  */
+       /*
+        * Maybe devices without subordinate buses shouldn't have this
+        * attribute in the first place?
+        */
        if (!pdev->subordinate)
                return count;
 
@@ -670,7 +674,7 @@ pci_write_config(struct file* filp, struct kobject *kobj,
                size = dev->cfg_size - off;
                count = size;
        }
-       
+
        pci_config_pm_runtime_get(dev);
 
        if ((off & 1) && size) {
@@ -678,7 +682,7 @@ pci_write_config(struct file* filp, struct kobject *kobj,
                off++;
                size--;
        }
-       
+
        if ((off & 3) && size > 2) {
                u16 val = data[off - init_off];
                val |= (u16) data[off - init_off + 1] << 8;
@@ -696,7 +700,7 @@ pci_write_config(struct file* filp, struct kobject *kobj,
                off += 4;
                size -= 4;
        }
-       
+
        if (size >= 2) {
                u16 val = data[off - init_off];
                val |= (u16) data[off - init_off + 1] << 8;
@@ -1229,21 +1233,21 @@ pci_read_rom(struct file *filp, struct kobject *kobj,
 
        if (!pdev->rom_attr_enabled)
                return -EINVAL;
-       
+
        rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
        if (!rom || !size)
                return -EIO;
-               
+
        if (off >= size)
                count = 0;
        else {
                if (off + count > size)
                        count = size - off;
-               
+
                memcpy_fromio(buf, rom + off, count);
        }
        pci_unmap_rom(pdev, rom);
-               
+
        return count;
 }
 
index b127fbda6fc8610dad9c81ae7c6e13acc3aabd5d..33120d15666895a2b48aec1c83f7b41f7b93c3be 100644 (file)
@@ -198,7 +198,7 @@ static int __pci_bus_find_cap_start(struct pci_bus *bus,
 }
 
 /**
- * pci_find_capability - query for devices' capabilities 
+ * pci_find_capability - query for devices' capabilities
  * @dev: PCI device to query
  * @cap: capability code
  *
@@ -207,12 +207,12 @@ static int __pci_bus_find_cap_start(struct pci_bus *bus,
  * device's PCI configuration space or 0 in case the device does not
  * support it.  Possible values for @cap:
  *
- *  %PCI_CAP_ID_PM           Power Management 
- *  %PCI_CAP_ID_AGP          Accelerated Graphics Port 
- *  %PCI_CAP_ID_VPD          Vital Product Data 
- *  %PCI_CAP_ID_SLOTID       Slot Identification 
+ *  %PCI_CAP_ID_PM           Power Management
+ *  %PCI_CAP_ID_AGP          Accelerated Graphics Port
+ *  %PCI_CAP_ID_VPD          Vital Product Data
+ *  %PCI_CAP_ID_SLOTID       Slot Identification
  *  %PCI_CAP_ID_MSI          Message Signalled Interrupts
- *  %PCI_CAP_ID_CHSWP        CompactPCI HotSwap 
+ *  %PCI_CAP_ID_CHSWP        CompactPCI HotSwap
  *  %PCI_CAP_ID_PCIX         PCI-X
  *  %PCI_CAP_ID_EXP          PCI Express
  */
@@ -228,13 +228,13 @@ int pci_find_capability(struct pci_dev *dev, int cap)
 }
 
 /**
- * pci_bus_find_capability - query for devices' capabilities 
+ * pci_bus_find_capability - query for devices' capabilities
  * @bus:   the PCI bus to query
  * @devfn: PCI device to query
  * @cap:   capability code
  *
  * Like pci_find_capability() but works for pci devices that do not have a
- * pci_dev structure set up yet. 
+ * pci_dev structure set up yet.
  *
  * Returns the address of the requested capability structure within the
  * device's PCI configuration space or 0 in case the device does not
@@ -515,7 +515,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
                return -EINVAL;
 
        /* Validate current state:
-        * Can enter D0 from any state, but if we can only go deeper 
+        * Can enter D0 from any state, but if we can only go deeper
         * to sleep if we're already in a low power state
         */
        if (state != PCI_D0 && dev->current_state <= PCI_D3cold
@@ -998,7 +998,7 @@ static void pci_restore_config_space(struct pci_dev *pdev)
        }
 }
 
-/** 
+/**
  * pci_restore_state - Restore the saved state of a PCI device
  * @dev: - PCI device that we're dealing with
  */
@@ -1030,7 +1030,7 @@ struct pci_saved_state {
  *                        the device saved state.
  * @dev: PCI device that we're dealing with
  *
- * Rerturn NULL if no state or error.
+ * Return NULL if no state or error.
  */
 struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev)
 {
@@ -1880,7 +1880,7 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
  * pci_dev_run_wake - Check if device can generate run-time wake-up events.
  * @dev: Device to check.
  *
- * Return true if the device itself is cabable of generating wake-up events
+ * Return true if the device itself is capable of generating wake-up events
  * (through the platform or using the native PCIe PME) or if the device supports
  * PME and one of its upstream bridges can generate wake-up events.
  */
@@ -2447,7 +2447,7 @@ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
        switch (pci_pcie_type(pdev)) {
        /*
         * PCI/X-to-PCIe bridges are not specifically mentioned by the spec,
-        * but since their primary inteface is PCI/X, we conservatively
+        * but since their primary interface is PCI/X, we conservatively
         * handle them as we would a non-PCIe device.
         */
        case PCI_EXP_TYPE_PCIE_BRIDGE:
@@ -2471,7 +2471,7 @@ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
        /*
         * PCIe 3.0, 6.12.1.2 specifies ACS capabilities that should be
         * implemented by the remaining PCIe types to indicate peer-to-peer
-        * capabilities, but only when they are part of a multifunciton
+        * capabilities, but only when they are part of a multifunction
         * device.  The footnote for section 6.12 indicates the specific
         * PCIe types included here.
         */
@@ -2486,7 +2486,7 @@ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
        }
 
        /*
-        * PCIe 3.0, 6.12.1.3 specifies no ACS capabilties are applicable
+        * PCIe 3.0, 6.12.1.3 specifies no ACS capabilities are applicable
         * to single function devices with the exception of downstream ports.
         */
        return true;
@@ -2622,7 +2622,7 @@ void pci_release_region(struct pci_dev *pdev, int bar)
  *
  *     If @exclusive is set, then the region is marked so that userspace
  *     is explicitly not allowed to map the resource via /dev/mem or
- *     sysfs MMIO access.
+ *     sysfs MMIO access.
  *
  *     Returns 0 on success, or %EBUSY on error.  A warning
  *     message is also printed on failure.
@@ -2634,7 +2634,7 @@ static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_n
 
        if (pci_resource_len(pdev, bar) == 0)
                return 0;
-               
+
        if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) {
                if (!request_region(pci_resource_start(pdev, bar),
                            pci_resource_len(pdev, bar), res_name))
@@ -2694,7 +2694,7 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
  *
  *     The key difference that _exclusive makes it that userspace is
  *     explicitly not allowed to map the resource via /dev/mem or
- *     sysfs.
+ *     sysfs.
  */
 int pci_request_region_exclusive(struct pci_dev *pdev, int bar, const char *res_name)
 {
@@ -2799,7 +2799,7 @@ int pci_request_regions(struct pci_dev *pdev, const char *res_name)
  *     successfully.
  *
  *     pci_request_regions_exclusive() will mark the region so that
- *     /dev/mem and the sysfs MMIO access will not be allowed.
+ *     /dev/mem and the sysfs MMIO access will not be allowed.
  *
  *     Returns 0 on success, or %EBUSY on error.  A warning
  *     message is also printed on failure.
@@ -2967,7 +2967,7 @@ pci_set_mwi(struct pci_dev *dev)
                cmd |= PCI_COMMAND_INVALIDATE;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
-       
+
        return 0;
 }
 
@@ -3292,7 +3292,7 @@ clear:
  *
  * NOTE: This causes the caller to sleep for twice the device power transition
  * cooldown period, which for the D0->D3hot and D3hot->D0 transitions is 10 ms
- * by devault (i.e. unless the @dev's d3_delay field has a different value).
+ * by default (i.e. unless the @dev's d3_delay field has a different value).
  * Moreover, only devices in D0 can be reset by this function.
  */
 static int pci_pm_reset(struct pci_dev *dev, int probe)
@@ -3341,7 +3341,7 @@ void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
        /*
         * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms.  Double
-        * this to 2ms to ensure that we meet the minium requirement.
+        * this to 2ms to ensure that we meet the minimum requirement.
         */
        msleep(2);
 
@@ -3998,7 +3998,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps)
                return -EINVAL;
 
        v = ffs(mps) - 8;
-       if (v > dev->pcie_mpss) 
+       if (v > dev->pcie_mpss)
                return -EINVAL;
        v <<= 5;
 
index 85ca36f2136d0008af72c4aa0ddbaa1b321855e7..b2c8881da764e582982cebe9a5062e69029dfca8 100644 (file)
@@ -525,7 +525,7 @@ static void handle_error_source(struct pcie_device *aerdev,
 
        if (info->severity == AER_CORRECTABLE) {
                /*
-                * Correctable error does not need software intevention.
+                * Correctable error does not need software intervention.
                 * No need to go through error recovery process.
                 */
                pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
@@ -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 403a44374ed5e160cbd0f6a0a865a5ab35de139c..f1272dc54de176125a63be94ad6f40b7aa64659e 100644 (file)
@@ -548,7 +548,7 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
 
 /*
  * pcie_aspm_init_link_state: Initiate PCI express link state.
- * It is called after the pcie and its children devices are scaned.
+ * It is called after the pcie and its children devices are scanned.
  * @pdev: the root port or switch downstream port
  */
 void pcie_aspm_init_link_state(struct pci_dev *pdev)
index e56e594ce112fd2f8f15696d9036054e03aa9f63..bbc3bdd2b189f8a91fa73fa8c8bca28b818359d9 100644 (file)
@@ -419,8 +419,8 @@ static void pcie_pme_remove(struct pcie_device *srv)
 
 static struct pcie_port_service_driver pcie_pme_driver = {
        .name           = "pcie_pme",
-       .port_type      = PCI_EXP_TYPE_ROOT_PORT,
-       .service        = PCIE_PORT_SERVICE_PME,
+       .port_type      = PCI_EXP_TYPE_ROOT_PORT,
+       .service        = PCIE_PORT_SERVICE_PME,
 
        .probe          = pcie_pme_probe,
        .suspend        = pcie_pme_suspend,
index d2eb80aab56956d299a48208ea5d296cda0dce66..d525548404d69eda837d1cbe69e668b72116d56e 100644 (file)
@@ -14,7 +14,7 @@
 #define PCIE_PORT_DEVICE_MAXSERVICES   4
 /*
  * According to the PCI Express Base Specification 2.0, the indices of
- * the MSI-X table entires used by port services must not exceed 31
+ * the MSI-X table entries used by port services must not exceed 31
  */
 #define PCIE_PORT_MAX_MSIX_ENTRIES     32
 
index 67be55a7f260ffabfc0410bd5f4cde21b46d582c..87e79a6ffb5a0da6cc7ac93a7d39d05ce17ae8ad 100644 (file)
@@ -18,8 +18,8 @@
 static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
 
 struct bus_type pcie_port_bus_type = {
-       .name           = "pci_express",
-       .match          = pcie_port_bus_match,
+       .name           = "pci_express",
+       .match          = pcie_port_bus_match,
 };
 EXPORT_SYMBOL_GPL(pcie_port_bus_type);
 
index 08d131f7815be99a58890e6f5142479e033bd4cd..0b6e76604068043d9c261ddb9461757d4e64c279 100644 (file)
@@ -46,7 +46,7 @@ static void release_pcie_device(struct device *dev)
  * pcie_port_msix_add_entry - add entry to given array of MSI-X entries
  * @entries: Array of MSI-X entries
  * @new_entry: Index of the entry to add to the array
- * @nr_entries: Number of entries aleady in the array
+ * @nr_entries: Number of entries already in the array
  *
  * Return value: Position of the added entry in the array
  */
index 696caed5fdf557198db4bef5201df2a6c04c83d2..0d8fdc48e642ffcc421464ca226ebec78211148c 100644 (file)
@@ -223,7 +223,6 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
 static void pcie_portdrv_remove(struct pci_dev *dev)
 {
        pcie_port_device_remove(dev);
-       pci_disable_device(dev);
 }
 
 static int error_detected_iter(struct device *device, void *data)
@@ -390,9 +389,9 @@ static struct pci_driver pcie_portdriver = {
        .probe          = pcie_portdrv_probe,
        .remove         = pcie_portdrv_remove,
 
-       .err_handler    = &pcie_portdrv_err_handler,
+       .err_handler    = &pcie_portdrv_err_handler,
 
-       .driver.pm      = PCIE_PORTDRV_PM_OPS,
+       .driver.pm      = PCIE_PORTDRV_PM_OPS,
 };
 
 static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
@@ -412,7 +411,7 @@ static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = {
         .ident = "MSI Wind U-100",
         .matches = {
                     DMI_MATCH(DMI_SYS_VENDOR,
-                               "MICRO-STAR INTERNATIONAL CO., LTD"),
+                               "MICRO-STAR INTERNATIONAL CO., LTD"),
                     DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
                     },
         },
index 5e14f5a51357cabd86d8e420e7684d3122b30006..38e403dddf6e0e07166585e226d47f2793bd86f8 100644 (file)
@@ -582,7 +582,7 @@ static enum pci_bus_speed agp_speed(int agp3, int agpstat)
                index = 1;
        else
                goto out;
-       
+
        if (agp3) {
                index += 2;
                if (index == 5)
@@ -789,7 +789,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
        }
 
        /* Disable MasterAbortMode during probing to avoid reporting
-          of bus errors (in some architectures) */ 
+          of bus errors (in some architectures) */
        pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
                              bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
@@ -1005,7 +1005,7 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev)
  * pci_setup_device - fill in class and map information of a device
  * @dev: the device structure to fill
  *
- * Initialize the device structure with information about the device's 
+ * Initialize the device structure with information about the device's
  * vendor,class,memory and IO-space addresses,IRQ lines etc.
  * Called at initialisation of the PCI subsystem and by CardBus services.
  * Returns 0 on success and negative if unknown type of device (not normal,
@@ -1111,7 +1111,7 @@ int pci_setup_device(struct pci_dev *dev)
                        goto bad;
                /* The PCI-to-PCI bridge spec requires that subtractive
                   decoding (i.e. transparent) bridge must have programming
-                  interface code of 0x01. */ 
+                  interface code of 0x01. */
                pci_read_irq(dev);
                dev->transparent = ((dev->class & 0xff) == 1);
                pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
@@ -1570,7 +1570,7 @@ static void pcie_write_mrrs(struct pci_dev *dev)
         * subsequent read will verify if the value is acceptable or not.
         * If the MRRS value provided is not acceptable (e.g., too large),
         * shrink the value until it is acceptable to the HW.
-        */
+        */
        while (mrrs != pcie_get_readrq(dev) && mrrs >= 128) {
                rc = pcie_set_readrq(dev, mrrs);
                if (!rc)
index cdc7836d7e3de3ab710341b3feef63c1ff3b3a47..46d1378f2e9ebd1aeb6ee8252af551a0350bb80b 100644 (file)
@@ -222,7 +222,7 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
        default:
                ret = -EINVAL;
                break;
-       };
+       }
 
        return ret;
 }
index 91490453c2296878f19ff8105e9dbbec4d4878b1..3a02717473adc7dfb775a543d6c8f786ddb91ff4 100644 (file)
@@ -9,10 +9,6 @@
  *
  *  Init/reset quirks for USB host controllers should be in the
  *  USB quirks file, where their drivers can access reuse it.
- *
- *  The bridge optimization stuff has been removed. If you really
- *  have a silly BIOS which is unable to set your host bridge right,
- *  use the PowerTweak utility (see http://powertweak.sourceforge.net).
  */
 
 #include <linux/types.h>
@@ -55,7 +51,7 @@ static void quirk_mellanox_tavor(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR,quirk_mellanox_tavor);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE,quirk_mellanox_tavor);
 
-/* Deal with broken BIOS'es that neglect to enable passive release,
+/* Deal with broken BIOSes that neglect to enable passive release,
    which can cause problems in combination with the 82441FX/PPro MTRRs */
 static void quirk_passive_release(struct pci_dev *dev)
 {
@@ -78,11 +74,11 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,       PCI_DEVICE_ID_INTEL_82441,      quirk_p
 
 /*  The VIA VP2/VP3/MVP3 seem to have some 'features'. There may be a workaround
     but VIA don't answer queries. If you happen to have good contacts at VIA
-    ask them for me please -- Alan 
-    
-    This appears to be BIOS not version dependent. So presumably there is a 
+    ask them for me please -- Alan
+
+    This appears to be BIOS not version dependent. So presumably there is a
     chipset level fix */
-    
+
 static void quirk_isa_dma_hangs(struct pci_dev *dev)
 {
        if (!isa_dma_bridge_buggy) {
@@ -97,7 +93,7 @@ static void quirk_isa_dma_hangs(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_0,     quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C596,       quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82371SB_0,  quirk_isa_dma_hangs);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M1533,         quirk_isa_dma_hangs);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M1533,         quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,     PCI_DEVICE_ID_NEC_CBUS_1,       quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,     PCI_DEVICE_ID_NEC_CBUS_2,       quirk_isa_dma_hangs);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC,     PCI_DEVICE_ID_NEC_CBUS_3,       quirk_isa_dma_hangs);
@@ -157,10 +153,10 @@ static void quirk_triton(struct pci_dev *dev)
                pci_pci_problems |= PCIPCI_TRITON;
        }
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82437,      quirk_triton);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82437VX,    quirk_triton);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82439,      quirk_triton);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82439TX,    quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82437,      quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82437VX,    quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82439,      quirk_triton);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82439TX,    quirk_triton);
 
 /*
  *     VIA Apollo KT133 needs PCI latency patch
@@ -171,7 +167,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,        PCI_DEVICE_ID_INTEL_82439TX,    quir
  *      the info on which Mr Breese based his work.
  *
  *     Updated based on further information from the site and also on
- *     information provided by VIA 
+ *     information provided by VIA
  */
 static void quirk_vialatency(struct pci_dev *dev)
 {
@@ -179,7 +175,7 @@ static void quirk_vialatency(struct pci_dev *dev)
        u8 busarb;
        /* Ok we have a potential problem chipset here. Now see if we have
           a buggy southbridge */
-          
+
        p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, NULL);
        if (p!=NULL) {
                /* 0x40 - 0x4f == 686B, 0x10 - 0x2f == 686A; thanks Dan Hollis */
@@ -194,9 +190,9 @@ static void quirk_vialatency(struct pci_dev *dev)
                if (p->revision < 0x10 || p->revision > 0x12)
                        goto exit;
        }
-       
+
        /*
-        *      Ok we have the problem. Now set the PCI master grant to 
+        *      Ok we have the problem. Now set the PCI master grant to
         *      occur every master grant. The apparent bug is that under high
         *      PCI load (quite common in Linux of course) you can get data
         *      loss when the CPU is held off the bus for 3 bus master requests
@@ -209,7 +205,7 @@ static void quirk_vialatency(struct pci_dev *dev)
         */
 
        pci_read_config_byte(dev, 0x76, &busarb);
-       /* Set bit 4 and bi 5 of byte 76 to 0x01 
+       /* Set bit 4 and bi 5 of byte 76 to 0x01
           "Master priority rotation on every PCI master grant */
        busarb &= ~(1<<5);
        busarb |= (1<<4);
@@ -252,7 +248,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,  PCI_DEVICE_ID_VIA_82C576,       quirk_vsfx)
  *     that DMA to AGP space. Latency must be set to 0xA and triton
  *     workaround applied too
  *     [Info kindly provided by ALi]
- */    
+ */
 static void quirk_alimagik(struct pci_dev *dev)
 {
        if ((pci_pci_problems&PCIPCI_ALIMAGIK)==0) {
@@ -260,8 +256,8 @@ static void quirk_alimagik(struct pci_dev *dev)
                pci_pci_problems |= PCIPCI_ALIMAGIK|PCIPCI_TRITON;
        }
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M1647,         quirk_alimagik);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M1651,         quirk_alimagik);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M1647,         quirk_alimagik);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,      PCI_DEVICE_ID_AL_M1651,         quirk_alimagik);
 
 /*
  *     Natoma has some interesting boundary conditions with Zoran stuff
@@ -274,12 +270,12 @@ static void quirk_natoma(struct pci_dev *dev)
                pci_pci_problems |= PCIPCI_NATOMA;
        }
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82441,      quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443LX_0,  quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443LX_1,  quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443BX_0,  quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443BX_1,  quirk_natoma);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443BX_2,  quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82441,      quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443LX_0,  quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443LX_1,  quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443BX_0,  quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443BX_1,  quirk_natoma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82443BX_2,  quirk_natoma);
 
 /*
  *  This chip can cause PCI parity errors if config register 0xA0 is read
@@ -400,7 +396,7 @@ static void piix4_io_quirk(struct pci_dev *dev, const char *name, unsigned int p
        /*
         * For now we only print it out. Eventually we'll want to
         * reserve it (at least if it's in the 0x1000+ range), but
-        * let's get enough confirmation reports first. 
+        * let's get enough confirmation reports first.
         */
        base &= -size;
        dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base + size - 1);
@@ -425,7 +421,7 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int
        }
        /*
         * For now we only print it out. Eventually we'll want to
-        * reserve it, but let's get enough confirmation reports first. 
+        * reserve it, but let's get enough confirmation reports first.
         */
        base &= -size;
        dev_info(&dev->dev, "%s MMIO at %04x-%04x\n", name, base, base + size - 1);
@@ -682,7 +678,7 @@ static void quirk_xio2000a(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XIO2000A,
                        quirk_xio2000a);
 
-#ifdef CONFIG_X86_IO_APIC 
+#ifdef CONFIG_X86_IO_APIC
 
 #include <asm/io_apic.h>
 
@@ -696,12 +692,12 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XIO2000A,
 static void quirk_via_ioapic(struct pci_dev *dev)
 {
        u8 tmp;
-       
+
        if (nr_ioapics < 1)
                tmp = 0;    /* nothing routed to external APIC */
        else
                tmp = 0x1f; /* all known bits (4-0) routed to external APIC */
-               
+
        dev_info(&dev->dev, "%sbling VIA external APIC routing\n",
               tmp == 0 ? "Disa" : "Ena");
 
@@ -712,7 +708,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,  PCI_DEVICE_ID_VIA_82C686,       quirk_via_i
 DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C686,       quirk_via_ioapic);
 
 /*
- * VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit.
+ * VIA 8237: Some BIOSes don't set the 'Bypass APIC De-Assert Message' Bit.
  * This leads to doubled level interrupt rates.
  * Set this bit to get rid of cycle wastage.
  * Otherwise uncritical.
@@ -986,7 +982,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_CYRIX,       PCI_DEVICE_ID_CYRIX_PCI_MASTER, qu
 static void quirk_disable_pxb(struct pci_dev *pdev)
 {
        u16 config;
-       
+
        if (pdev->revision != 0x04)             /* Only C0 requires this */
                return;
        pci_read_config_word(pdev, 0x40, &config);
@@ -1094,11 +1090,11 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82375,      quirk_e
  * On ASUS P4B boards, the SMBus PCI Device within the ICH2/4 southbridge
  * is not activated. The myth is that Asus said that they do not want the
  * users to be irritated by just another PCI Device in the Win98 device
- * manager. (see the file prog/hotplug/README.p4b in the lm_sensors 
+ * manager. (see the file prog/hotplug/README.p4b in the lm_sensors
  * package 2.7.0 for details)
  *
- * The SMBus PCI Device can be activated by setting a bit in the ICH LPC 
- * bridge. Unfortunately, this device has no subvendor/subdevice ID. So it 
+ * The SMBus PCI Device can be activated by setting a bit in the ICH LPC
+ * bridge. Unfortunately, this device has no subvendor/subdevice ID. So it
  * becomes necessary to do this tweak in two steps -- the chosen trigger
  * is either the Host bridge (preferred) or on-board VGA controller.
  *
@@ -1253,7 +1249,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,     PCI_DEVICE_ID_INTEL_82815_CGC,  asu
 static void asus_hides_smbus_lpc(struct pci_dev *dev)
 {
        u16 val;
-       
+
        if (likely(!asus_hides_smbus))
                return;
 
@@ -1640,8 +1636,8 @@ static void quirk_disable_intel_boot_interrupt(struct pci_dev *dev)
        dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
                 dev->vendor, dev->device);
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_ESB_10,     quirk_disable_intel_boot_interrupt);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_ESB_10,    quirk_disable_intel_boot_interrupt);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_ESB_10,     quirk_disable_intel_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_ESB_10,    quirk_disable_intel_boot_interrupt);
 
 /*
  * disable boot interrupts on HT-1000
@@ -1673,8 +1669,8 @@ static void quirk_disable_broadcom_boot_interrupt(struct pci_dev *dev)
        dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
                 dev->vendor, dev->device);
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS,   PCI_DEVICE_ID_SERVERWORKS_HT1000SB,       quirk_disable_broadcom_boot_interrupt);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS,   PCI_DEVICE_ID_SERVERWORKS_HT1000SB,      quirk_disable_broadcom_boot_interrupt);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS,   PCI_DEVICE_ID_SERVERWORKS_HT1000SB,       quirk_disable_broadcom_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS,   PCI_DEVICE_ID_SERVERWORKS_HT1000SB,      quirk_disable_broadcom_boot_interrupt);
 
 /*
  * disable boot interrupts on AMD and ATI chipsets
@@ -1730,8 +1726,8 @@ static void quirk_disable_amd_8111_boot_interrupt(struct pci_dev *dev)
        dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
                 dev->vendor, dev->device);
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_8111_SMBUS,     quirk_disable_amd_8111_boot_interrupt);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_8111_SMBUS,    quirk_disable_amd_8111_boot_interrupt);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_8111_SMBUS,     quirk_disable_amd_8111_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_8111_SMBUS,    quirk_disable_amd_8111_boot_interrupt);
 #endif /* CONFIG_X86_IO_APIC */
 
 /*
@@ -2127,8 +2123,8 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8624, quirk_tile_plx_gen1);
 #ifdef CONFIG_PCI_MSI
 /* Some chipsets do not support MSI. We cannot easily rely on setting
  * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
- * some other busses controlled by the chipset even if Linux is not
- * aware of it.  Instead of setting the flag on all busses in the
+ * some other buses controlled by the chipset even if Linux is not
+ * aware of it.  Instead of setting the flag on all buses in the
  * machine, simply disable MSI globally.
  */
 static void quirk_disable_all_msi(struct pci_dev *dev)
@@ -2288,14 +2284,14 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
                        nvenet_msi_disable);
 
 /*
- * Some versions of the MCP55 bridge from nvidia have a legacy irq routing
- * config register.  This register controls the routing of legacy interrupts
- * from devices that route through the MCP55.  If this register is misprogramed
- * interrupts are only sent to the bsp, unlike conventional systems where the
- * irq is broadxast to all online cpus.  Not having this register set
- * properly prevents kdump from booting up properly, so lets make sure that
- * we have it set correctly.
- * Note this is an undocumented register.
+ * Some versions of the MCP55 bridge from Nvidia have a legacy IRQ routing
+ * config register.  This register controls the routing of legacy
+ * interrupts from devices that route through the MCP55.  If this register
+ * is misprogrammed, interrupts are only sent to the BSP, unlike
+ * conventional systems where the IRQ is broadcast to all online CPUs.  Not
+ * having this register set properly prevents kdump from booting up
+ * properly, so let's make sure that we have it set correctly.
+ * Note that this is an undocumented register.
  */
 static void nvbridge_check_legacy_irq_routing(struct pci_dev *dev)
 {
@@ -2626,7 +2622,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, 0xe091,
 /* Allow manual resource allocation for PCI hotplug bridges
  * via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For
  * some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6),
- * kernel fails to allocate resources when hotplug device is 
+ * kernel fails to allocate resources when hotplug device is
  * inserted and PCI bus is rescanned.
  */
 static void quirk_hotplug_bridge(struct pci_dev *dev)
index 8fc54b7327bc02b59af4a6455bcf43bdaf597252..1576851028db700be2413b01e815cc8387b3cd47 100644 (file)
@@ -7,7 +7,7 @@ static void pci_free_resources(struct pci_dev *dev)
 {
        int i;
 
-       msi_remove_pci_irq_vectors(dev);
+       msi_remove_pci_irq_vectors(dev);
 
        pci_cleanup_rom(dev);
        for (i = 0; i < PCI_NUM_RESOURCES; i++) {
index d0627fa9f36827fdd864e462d519fd2279ef7d56..3ff2ac7c14e235c564b3b8d9c4d11609a4696fe5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     PCI searching functions.
+ *     PCI searching functions.
  *
  *     Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
  *                                     David Mosberger-Tang
@@ -96,12 +96,12 @@ struct pci_bus * pci_find_bus(int domain, int busnr)
  * pci_find_next_bus - begin or continue searching for a PCI bus
  * @from: Previous PCI bus found, or %NULL for new search.
  *
- * Iterates through the list of known PCI busses.  A new search is
+ * Iterates through the list of known PCI buses.  A new search is
  * initiated by passing %NULL as the @from argument.  Otherwise if
  * @from is not %NULL, searches continue from next device on the
  * global list.
  */
-struct pci_bus * 
+struct pci_bus *
 pci_find_next_bus(const struct pci_bus *from)
 {
        struct list_head *n;
@@ -119,11 +119,11 @@ pci_find_next_bus(const struct pci_bus *from)
 /**
  * pci_get_slot - locate PCI device for a given PCI slot
  * @bus: PCI bus on which desired PCI device resides
- * @devfn: encodes number of PCI slot in which the desired PCI 
- * device resides and the logical device number within that slot 
+ * @devfn: encodes number of PCI slot in which the desired PCI
+ * device resides and the logical device number within that slot
  * in case of multi-function devices.
  *
- * Given a PCI bus and slot/function number, the desired PCI device 
+ * Given a PCI bus and slot/function number, the desired PCI device
  * is located in the list of PCI devices.
  * If the device is found, its reference count is increased and this
  * function returns a pointer to its data structure.  The caller must
index 4ce83b26ae9ef2dcbdf9e29c81d55751a9525a7e..219a4106480a5c568b6e9ad11023f735c1ccc1c9 100644 (file)
@@ -292,8 +292,8 @@ static void assign_requested_resources_sorted(struct list_head *head,
                                      (!(res->flags & IORESOURCE_ROM_ENABLE))))
                                        add_to_list(fail_head,
                                                    dev_res->dev, res,
-                                                   0 /* dont care */,
-                                                   0 /* dont care */);
+                                                   0 /* don't care */,
+                                                   0 /* don't care */);
                        }
                        reset_resource(res);
                }
@@ -667,9 +667,9 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
        if (!io) {
                pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
                pci_read_config_word(bridge, PCI_IO_BASE, &io);
-               pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
-       }
-       if (io)
+               pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
+       }
+       if (io)
                b_res[0].flags |= IORESOURCE_IO;
        /*  DECchip 21050 pass 2 errata: the bridge may miss an address
            disconnect boundary by one PCI data phase.
@@ -819,7 +819,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
        resource_size_t min_align, align;
 
        if (!b_res)
-               return;
+               return;
 
        min_align = window_alignment(bus, IORESOURCE_IO);
        list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -950,7 +950,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                        if (realloc_head && i >= PCI_IOV_RESOURCES &&
                                        i <= PCI_IOV_RESOURCE_END) {
                                r->end = r->start - 1;
-                               add_to_list(realloc_head, dev, r, r_size, 0/* dont' care */);
+                               add_to_list(realloc_head, dev, r, r_size, 0/* don't care */);
                                children_add_size += r_size;
                                continue;
                        }
@@ -1456,8 +1456,8 @@ static enum enable_type pci_realloc_detect(struct pci_bus *bus,
 
 /*
  * first try will not touch pci bridge res
- * second  and later try will clear small leaf bridge res
- * will stop till to the max  deepth if can not find good one
+ * second and later try will clear small leaf bridge res
+ * will stop till to the max depth if can not find good one
  */
 void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
 {
index 07f2eddc09cefec8dd6a742b0f5a1f659cb6080c..83c4d3bc47ab5bc14d28b0863cfeedf97cec4680 100644 (file)
@@ -159,7 +159,7 @@ resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
        return 0;
 }
 
-static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, 
+static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
                int resno, resource_size_t size)
 {
        struct resource *root, *conflict;
index c1e9284a677b0b0ee33aa2b28c5e675ed6e3616b..448ca562d1f8cd368385a0d1bd53f26a3c1171c4 100644 (file)
@@ -53,7 +53,7 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf)
 static const char *pci_bus_speed_strings[] = {
        "33 MHz PCI",           /* 0x00 */
        "66 MHz PCI",           /* 0x01 */
-       "66 MHz PCI-X",         /* 0x02 */
+       "66 MHz PCI-X",         /* 0x02 */
        "100 MHz PCI-X",        /* 0x03 */
        "133 MHz PCI-X",        /* 0x04 */
        NULL,                   /* 0x05 */
index e1c1ec5408934204c35025122cd5b64d6a14080e..24750a1b39b67cd9b84229204beb0945b4c09f8e 100644 (file)
@@ -44,7 +44,7 @@ SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
        default:
                err = -EINVAL;
                goto error;
-       };
+       }
 
        err = -EIO;
        if (cfg_ret != PCIBIOS_SUCCESSFUL)
index 0846922b2316d0774e4f154aee45256c6019a41a..829b98c5c66fc99da137857847c7048c4e4d1d47 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 69616aeaa966218efa16eb3858324107e816e7bf..09fde58b12e0fa1e2446d56218fa644f32aeeef1 100644 (file)
@@ -5,3 +5,4 @@ if GOLDFISH
 source "drivers/platform/goldfish/Kconfig"
 endif
 
+source "drivers/platform/chrome/Kconfig"
index 8a44a4cd6d1efc30789d5da4cc392447695d68ae..3656b7b17b99ee8ecc806fd21f3fa493873ff122 100644 (file)
@@ -5,3 +5,4 @@
 obj-$(CONFIG_X86)              += x86/
 obj-$(CONFIG_OLPC)             += olpc/
 obj-$(CONFIG_GOLDFISH)         += goldfish/
+obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
new file mode 100644 (file)
index 0000000..b13303e
--- /dev/null
@@ -0,0 +1,28 @@
+#
+# Platform support for Chrome OS hardware (Chromebooks and Chromeboxes)
+#
+
+menuconfig CHROME_PLATFORMS
+       bool "Platform support for Chrome hardware"
+       depends on X86
+       ---help---
+         Say Y here to get to see options for platform support for
+         various Chromebooks and Chromeboxes. This option alone does
+         not add any kernel code.
+
+         If you say N, all options in this submenu will be skipped and disabled.
+
+if CHROME_PLATFORMS
+
+config CHROMEOS_LAPTOP
+       tristate "Chrome OS Laptop"
+       depends on I2C
+       depends on DMI
+       ---help---
+         This driver instantiates i2c and smbus devices such as
+         light sensors and touchpads.
+
+         If you have a supported Chromebook, choose Y or M here.
+         The module will be called chromeos_laptop.
+
+endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
new file mode 100644 (file)
index 0000000..015e919
--- /dev/null
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_CHROMEOS_LAPTOP)  += chromeos_laptop.o
index b51a7460cc49bc03b4c055e8f46bfe2fedf718ff..d9dcd37b5a521e86baf54702ff96f91657037aff 100644 (file)
@@ -79,17 +79,6 @@ config ASUS_LAPTOP
 
          If you have an ACPI-compatible ASUS laptop, say Y or M here.
 
-config CHROMEOS_LAPTOP
-       tristate "Chrome OS Laptop"
-       depends on I2C
-       depends on DMI
-       ---help---
-         This driver instantiates i2c and smbus devices such as
-         light sensors and touchpads.
-
-         If you have a supported Chromebook, choose Y or M here.
-         The module will be called chromeos_laptop.
-
 config DELL_LAPTOP
        tristate "Dell Laptop Extras"
        depends on X86
index 5dbe193243510a94db296ba690ded6fa1af768e5..f0e6aa407ffb9ee8786e7aa71f5c76ed00ec6d81 100644 (file)
@@ -50,7 +50,6 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON)  += intel_mid_powerbtn.o
 obj-$(CONFIG_INTEL_OAKTRAIL)   += intel_oaktrail.o
 obj-$(CONFIG_SAMSUNG_Q10)      += samsung-q10.o
 obj-$(CONFIG_APPLE_GMUX)       += apple-gmux.o
-obj-$(CONFIG_CHROMEOS_LAPTOP)  += chromeos_laptop.o
 obj-$(CONFIG_INTEL_RST)                += intel-rst.o
 obj-$(CONFIG_INTEL_SMARTCONNECT)       += intel-smartconnect.o
 
index 8eea2efbbb6dcfc23b692885693ce7f69e55437c..b9429fbf1cd82a403c65bddd09acc6e11078b66a 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 0e9c169b42f82a782b1166b2c05880ad4e1ad0da..594323a926cf0b8c62c4accfd88d297fe0caa567 100644 (file)
@@ -1494,10 +1494,9 @@ static int asus_input_init(struct asus_laptop *asus)
        int error;
 
        input = input_allocate_device();
-       if (!input) {
-               pr_warn("Unable to allocate input device\n");
+       if (!input)
                return -ENOMEM;
-       }
+
        input->name = "Asus Laptop extra buttons";
        input->phys = ASUS_LAPTOP_FILE "/input0";
        input->id.bustype = BUS_HOST;
index bb77e18b3dd4d5a8ce882f883dddbd98a28e037f..c608b1d33f4a60893a3bdc87b52773f872259f6d 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/err.h>
 #include <linux/dmi.h>
 #include <linux/io.h>
+#include <linux/rfkill.h>
 #include <linux/power_supply.h>
 #include <linux/acpi.h>
 #include <linux/mm.h>
@@ -89,6 +90,13 @@ static struct platform_driver platform_driver = {
 
 static struct platform_device *platform_device;
 static struct backlight_device *dell_backlight_device;
+static struct rfkill *wifi_rfkill;
+static struct rfkill *bluetooth_rfkill;
+static struct rfkill *wwan_rfkill;
+static bool force_rfkill;
+
+module_param(force_rfkill, bool, 0444);
+MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
 
 static const struct dmi_system_id dell_device_table[] __initconst = {
        {
@@ -355,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
        return buffer;
 }
 
+/* Derived from information in DellWirelessCtl.cpp:
+   Class 17, select 11 is radio control. It returns an array of 32-bit values.
+
+   Input byte 0 = 0: Wireless information
+
+   result[0]: return code
+   result[1]:
+     Bit 0:      Hardware switch supported
+     Bit 1:      Wifi locator supported
+     Bit 2:      Wifi is supported
+     Bit 3:      Bluetooth is supported
+     Bit 4:      WWAN is supported
+     Bit 5:      Wireless keyboard supported
+     Bits 6-7:   Reserved
+     Bit 8:      Wifi is installed
+     Bit 9:      Bluetooth is installed
+     Bit 10:     WWAN is installed
+     Bits 11-15: Reserved
+     Bit 16:     Hardware switch is on
+     Bit 17:     Wifi is blocked
+     Bit 18:     Bluetooth is blocked
+     Bit 19:     WWAN is blocked
+     Bits 20-31: Reserved
+   result[2]: NVRAM size in bytes
+   result[3]: NVRAM format version number
+
+   Input byte 0 = 2: Wireless switch configuration
+   result[0]: return code
+   result[1]:
+     Bit 0:      Wifi controlled by switch
+     Bit 1:      Bluetooth controlled by switch
+     Bit 2:      WWAN controlled by switch
+     Bits 3-6:   Reserved
+     Bit 7:      Wireless switch config locked
+     Bit 8:      Wifi locator enabled
+     Bits 9-14:  Reserved
+     Bit 15:     Wifi locator setting locked
+     Bits 16-31: Reserved
+*/
+
+static int dell_rfkill_set(void *data, bool blocked)
+{
+       int disable = blocked ? 1 : 0;
+       unsigned long radio = (unsigned long)data;
+       int hwswitch_bit = (unsigned long)data - 1;
+
+       get_buffer();
+       dell_send_request(buffer, 17, 11);
+
+       /* If the hardware switch controls this radio, and the hardware
+          switch is disabled, always disable the radio */
+       if ((hwswitch_state & BIT(hwswitch_bit)) &&
+           !(buffer->output[1] & BIT(16)))
+               disable = 1;
+
+       buffer->input[0] = (1 | (radio<<8) | (disable << 16));
+       dell_send_request(buffer, 17, 11);
+
+       release_buffer();
+       return 0;
+}
+
+/* Must be called with the buffer held */
+static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
+                                       int status)
+{
+       if (status & BIT(0)) {
+               /* Has hw-switch, sync sw_state to BIOS */
+               int block = rfkill_blocked(rfkill);
+               buffer->input[0] = (1 | (radio << 8) | (block << 16));
+               dell_send_request(buffer, 17, 11);
+       } else {
+               /* No hw-switch, sync BIOS state to sw_state */
+               rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
+       }
+}
+
+static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
+                                       int status)
+{
+       if (hwswitch_state & (BIT(radio - 1)))
+               rfkill_set_hw_state(rfkill, !(status & BIT(16)));
+}
+
+static void dell_rfkill_query(struct rfkill *rfkill, void *data)
+{
+       int status;
+
+       get_buffer();
+       dell_send_request(buffer, 17, 11);
+       status = buffer->output[1];
+
+       dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status);
+
+       release_buffer();
+}
+
+static const struct rfkill_ops dell_rfkill_ops = {
+       .set_block = dell_rfkill_set,
+       .query = dell_rfkill_query,
+};
+
 static struct dentry *dell_laptop_dir;
 
 static int dell_debugfs_show(struct seq_file *s, void *data)
@@ -424,6 +534,136 @@ static const struct file_operations dell_debugfs_fops = {
        .release = single_release,
 };
 
+static void dell_update_rfkill(struct work_struct *ignored)
+{
+       int status;
+
+       get_buffer();
+       dell_send_request(buffer, 17, 11);
+       status = buffer->output[1];
+
+       if (wifi_rfkill) {
+               dell_rfkill_update_hw_state(wifi_rfkill, 1, status);
+               dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
+       }
+       if (bluetooth_rfkill) {
+               dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status);
+               dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
+       }
+       if (wwan_rfkill) {
+               dell_rfkill_update_hw_state(wwan_rfkill, 3, status);
+               dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
+       }
+
+       release_buffer();
+}
+static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
+
+
+static int __init dell_setup_rfkill(void)
+{
+       int status;
+       int ret;
+       const char *product;
+
+       /*
+        * rfkill causes trouble on various non Latitudes, according to Dell
+        * actually testing the rfkill functionality is only done on Latitudes.
+        */
+       product = dmi_get_system_info(DMI_PRODUCT_NAME);
+       if (!force_rfkill && (!product || strncmp(product, "Latitude", 8)))
+               return 0;
+
+       get_buffer();
+       dell_send_request(buffer, 17, 11);
+       status = buffer->output[1];
+       buffer->input[0] = 0x2;
+       dell_send_request(buffer, 17, 11);
+       hwswitch_state = buffer->output[1];
+       release_buffer();
+
+       if (!(status & BIT(0))) {
+               if (force_rfkill) {
+                       /* No hwsitch, clear all hw-controlled bits */
+                       hwswitch_state &= ~7;
+               } else {
+                       /* rfkill is only tested on laptops with a hwswitch */
+                       return 0;
+               }
+       }
+
+       if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
+               wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
+                                          RFKILL_TYPE_WLAN,
+                                          &dell_rfkill_ops, (void *) 1);
+               if (!wifi_rfkill) {
+                       ret = -ENOMEM;
+                       goto err_wifi;
+               }
+               ret = rfkill_register(wifi_rfkill);
+               if (ret)
+                       goto err_wifi;
+       }
+
+       if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
+               bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
+                                               &platform_device->dev,
+                                               RFKILL_TYPE_BLUETOOTH,
+                                               &dell_rfkill_ops, (void *) 2);
+               if (!bluetooth_rfkill) {
+                       ret = -ENOMEM;
+                       goto err_bluetooth;
+               }
+               ret = rfkill_register(bluetooth_rfkill);
+               if (ret)
+                       goto err_bluetooth;
+       }
+
+       if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
+               wwan_rfkill = rfkill_alloc("dell-wwan",
+                                          &platform_device->dev,
+                                          RFKILL_TYPE_WWAN,
+                                          &dell_rfkill_ops, (void *) 3);
+               if (!wwan_rfkill) {
+                       ret = -ENOMEM;
+                       goto err_wwan;
+               }
+               ret = rfkill_register(wwan_rfkill);
+               if (ret)
+                       goto err_wwan;
+       }
+
+       return 0;
+err_wwan:
+       rfkill_destroy(wwan_rfkill);
+       if (bluetooth_rfkill)
+               rfkill_unregister(bluetooth_rfkill);
+err_bluetooth:
+       rfkill_destroy(bluetooth_rfkill);
+       if (wifi_rfkill)
+               rfkill_unregister(wifi_rfkill);
+err_wifi:
+       rfkill_destroy(wifi_rfkill);
+
+       return ret;
+}
+
+static void dell_cleanup_rfkill(void)
+{
+       if (wifi_rfkill) {
+               rfkill_unregister(wifi_rfkill);
+               rfkill_destroy(wifi_rfkill);
+       }
+       if (bluetooth_rfkill) {
+               rfkill_unregister(bluetooth_rfkill);
+               rfkill_destroy(bluetooth_rfkill);
+       }
+       if (wwan_rfkill) {
+               rfkill_unregister(wwan_rfkill);
+               rfkill_destroy(wwan_rfkill);
+       }
+}
+
 static int dell_send_intensity(struct backlight_device *bd)
 {
        int ret = 0;
@@ -515,6 +755,30 @@ static void touchpad_led_exit(void)
        led_classdev_unregister(&touchpad_led);
 }
 
+static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
+                             struct serio *port)
+{
+       static bool extended;
+
+       if (str & 0x20)
+               return false;
+
+       if (unlikely(data == 0xe0)) {
+               extended = true;
+               return false;
+       } else if (unlikely(extended)) {
+               switch (data) {
+               case 0x8:
+                       schedule_delayed_work(&dell_rfkill_work,
+                                             round_jiffies_relative(HZ / 4));
+                       break;
+               }
+               extended = false;
+       }
+
+       return false;
+}
+
 static int __init dell_init(void)
 {
        int max_intensity = 0;
@@ -557,10 +821,26 @@ static int __init dell_init(void)
        }
        buffer = page_address(bufferpage);
 
+       ret = dell_setup_rfkill();
+
+       if (ret) {
+               pr_warn("Unable to setup rfkill\n");
+               goto fail_rfkill;
+       }
+
+       ret = i8042_install_filter(dell_laptop_i8042_filter);
+       if (ret) {
+               pr_warn("Unable to install key filter\n");
+               goto fail_filter;
+       }
+
        if (quirks && quirks->touchpad_led)
                touchpad_led_init(&platform_device->dev);
 
        dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
+       if (dell_laptop_dir != NULL)
+               debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
+                                   &dell_debugfs_fops);
 
 #ifdef CONFIG_ACPI
        /* In the event of an ACPI backlight being available, don't
@@ -603,6 +883,11 @@ static int __init dell_init(void)
        return 0;
 
 fail_backlight:
+       i8042_remove_filter(dell_laptop_i8042_filter);
+       cancel_delayed_work_sync(&dell_rfkill_work);
+fail_filter:
+       dell_cleanup_rfkill();
+fail_rfkill:
        free_page((unsigned long)bufferpage);
 fail_buffer:
        platform_device_del(platform_device);
@@ -620,7 +905,10 @@ static void __exit dell_exit(void)
        debugfs_remove_recursive(dell_laptop_dir);
        if (quirks && quirks->touchpad_led)
                touchpad_led_exit();
+       i8042_remove_filter(dell_laptop_i8042_filter);
+       cancel_delayed_work_sync(&dell_rfkill_work);
        backlight_device_unregister(dell_backlight_device);
+       dell_cleanup_rfkill();
        if (platform_device) {
                platform_device_unregister(platform_device);
                platform_driver_unregister(&platform_driver);
index fa9a2171cc134b733c837f89a4f1b80aeb0989ea..60e0900bc117995e6373bfb5bedfcc1c8aac2aed 100644 (file)
@@ -130,7 +130,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
        KEY_BRIGHTNESSUP,       KEY_UNKNOWN,    KEY_KBDILLUMTOGGLE,
        KEY_UNKNOWN,    KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN, KEY_UNKNOWN,
        KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN,    KEY_UNKNOWN, KEY_PROG2,
-       KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
+       KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_MICMUTE,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -139,8 +140,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       KEY_PROG3
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3
 };
 
 static struct input_dev *dell_wmi_input_dev;
index aefcc32e563479d2b22404fdd75ceb7072423d16..dec68e7a99c79482f5f7cf4eb78b98bd0e2cd58d 100644 (file)
@@ -1203,10 +1203,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
        int error;
 
        input = input_allocate_device();
-       if (!input) {
-               pr_info("Unable to allocate input device\n");
+       if (!input)
                return -ENOMEM;
-       }
 
        input->name = "Asus EeePC extra buttons";
        input->phys = EEEPC_LAPTOP_FILE "/input0";
index 1c86fa0857c8eb73dfb64ec90ee9e7f7e50404c3..8ba8956b5a48f7f1fccc37df42cdaf4772892209 100644 (file)
@@ -54,6 +54,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
 #define HPWMI_HARDWARE_QUERY 0x4
 #define HPWMI_WIRELESS_QUERY 0x5
 #define HPWMI_HOTKEY_QUERY 0xc
+#define HPWMI_FEATURE_QUERY 0xd
 #define HPWMI_WIRELESS2_QUERY 0x1b
 #define HPWMI_POSTCODEERROR_QUERY 0x2a
 
@@ -292,6 +293,17 @@ static int hp_wmi_tablet_state(void)
        return (state & 0x4) ? 1 : 0;
 }
 
+static int hp_wmi_bios_2009_later(void)
+{
+       int state = 0;
+       int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state,
+                                      sizeof(state), sizeof(state));
+       if (ret)
+               return ret;
+
+       return (state & 0x10) ? 1 : 0;
+}
+
 static int hp_wmi_set_block(void *data, bool blocked)
 {
        enum hp_wmi_radio r = (enum hp_wmi_radio) data;
@@ -871,7 +883,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
        gps_rfkill = NULL;
        rfkill2_count = 0;
 
-       if (hp_wmi_rfkill_setup(device))
+       if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
                hp_wmi_rfkill2_setup(device);
 
        err = device_create_file(&device->dev, &dev_attr_display);
index 6788acc22ab97f01b410240eef0a5b2082c98550..19ec95147f6925d1de52e99886f7efc6a10dff7a 100644 (file)
@@ -570,10 +570,8 @@ static int ideapad_input_init(struct ideapad_private *priv)
        int error;
 
        inputdev = input_allocate_device();
-       if (!inputdev) {
-               pr_info("Unable to allocate input device\n");
+       if (!inputdev)
                return -ENOMEM;
-       }
 
        inputdev->name = "Ideapad extra buttons";
        inputdev->phys = "ideapad/input0";
index 6b18aba82cfae450bde3cad8fd2b2d01f20b3d03..8d6775266d66263bac3bdd45d7a5f580e598731e 100644 (file)
@@ -66,10 +66,8 @@ static int mfld_pb_probe(struct platform_device *pdev)
                return -EINVAL;
 
        input = input_allocate_device();
-       if (!input) {
-               dev_err(&pdev->dev, "Input device allocation error\n");
+       if (!input)
                return -ENOMEM;
-       }
 
        input->name = pdev->name;
        input->phys = "power-button/input0";
index d654f831410de9621df488aadbca2d6c6d174721..60ea476a91305c8f357f1950064cee54e92f0f29 100644 (file)
  *    message handler is called within firmware.
  */
 
-#define IPC_BASE_ADDR     0xFF11C000   /* IPC1 base register address */
-#define IPC_MAX_ADDR      0x100                /* Maximum IPC regisers */
 #define IPC_WWBUF_SIZE    20           /* IPC Write buffer Size */
 #define IPC_RWBUF_SIZE    20           /* IPC Read buffer Size */
-#define IPC_I2C_BASE      0xFF12B000   /* I2C control register base address */
-#define IPC_I2C_MAX_ADDR  0x10         /* Maximum I2C regisers */
+#define IPC_IOC                  0x100         /* IPC command register IOC bit */
+
+enum {
+       SCU_IPC_LINCROFT,
+       SCU_IPC_PENWELL,
+       SCU_IPC_CLOVERVIEW,
+       SCU_IPC_TANGIER,
+};
+
+/* intel scu ipc driver data*/
+struct intel_scu_ipc_pdata_t {
+       u32 ipc_base;
+       u32 i2c_base;
+       u32 ipc_len;
+       u32 i2c_len;
+       u8 irq_mode;
+};
+
+static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = {
+       [SCU_IPC_LINCROFT] = {
+               .ipc_base = 0xff11c000,
+               .i2c_base = 0xff12b000,
+               .ipc_len = 0x100,
+               .i2c_len = 0x10,
+               .irq_mode = 0,
+       },
+       [SCU_IPC_PENWELL] = {
+               .ipc_base = 0xff11c000,
+               .i2c_base = 0xff12b000,
+               .ipc_len = 0x100,
+               .i2c_len = 0x10,
+               .irq_mode = 1,
+       },
+       [SCU_IPC_CLOVERVIEW] = {
+               .ipc_base = 0xff11c000,
+               .i2c_base = 0xff12b000,
+               .ipc_len = 0x100,
+               .i2c_len = 0x10,
+               .irq_mode = 1,
+       },
+       [SCU_IPC_TANGIER] = {
+               .ipc_base = 0xff009000,
+               .i2c_base  = 0xff00d000,
+               .ipc_len  = 0x100,
+               .i2c_len = 0x10,
+               .irq_mode = 0,
+       },
+};
 
 static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);
 static void ipc_remove(struct pci_dev *pdev);
@@ -72,6 +116,8 @@ struct intel_scu_ipc_dev {
        struct pci_dev *pdev;
        void __iomem *ipc_base;
        void __iomem *i2c_base;
+       struct completion cmd_complete;
+       u8 irq_mode;
 };
 
 static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
@@ -98,6 +144,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
  */
 static inline void ipc_command(u32 cmd) /* Send ipc command */
 {
+       if (ipcdev.irq_mode) {
+               reinit_completion(&ipcdev.cmd_complete);
+               writel(cmd | IPC_IOC, ipcdev.ipc_base);
+       }
        writel(cmd, ipcdev.ipc_base);
 }
 
@@ -156,6 +206,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */
        return 0;
 }
 
+/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
+static inline int ipc_wait_for_interrupt(void)
+{
+       int status;
+
+       if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) {
+               struct device *dev = &ipcdev.pdev->dev;
+               dev_err(dev, "IPC timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       status = ipc_read_status();
+
+       if ((status >> 1) & 1)
+               return -EIO;
+
+       return 0;
+}
+
+int intel_scu_ipc_check_status(void)
+{
+       return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop();
+}
+
 /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
 static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 {
@@ -196,8 +270,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
                ipc_command(4 << 16 |  id << 12 | 0 << 8 | op);
        }
 
-       err = busy_loop();
-       if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
+       err = intel_scu_ipc_check_status();
+       if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
                /* Workaround: values are read as 0 without memcpy_fromio */
                memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
                for (nc = 0; nc < count; nc++)
@@ -391,7 +465,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
                return -ENODEV;
        }
        ipc_command(sub << 12 | cmd);
-       err = busy_loop();
+       err = intel_scu_ipc_check_status();
        mutex_unlock(&ipclock);
        return err;
 }
@@ -425,10 +499,12 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
                ipc_data_writel(*in++, 4 * i);
 
        ipc_command((inlen << 16) | (sub << 12) | cmd);
-       err = busy_loop();
+       err = intel_scu_ipc_check_status();
 
-       for (i = 0; i < outlen; i++)
-               *out++ = ipc_data_readl(4 * i);
+       if (!err) {
+               for (i = 0; i < outlen; i++)
+                       *out++ = ipc_data_readl(4 * i);
+       }
 
        mutex_unlock(&ipclock);
        return err;
@@ -491,6 +567,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
  */
 static irqreturn_t ioc(int irq, void *dev_id)
 {
+       if (ipcdev.irq_mode)
+               complete(&ipcdev.cmd_complete);
+
        return IRQ_HANDLED;
 }
 
@@ -504,13 +583,18 @@ static irqreturn_t ioc(int irq, void *dev_id)
  */
 static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
-       int err;
+       int err, pid;
+       struct intel_scu_ipc_pdata_t *pdata;
        resource_size_t pci_resource;
 
        if (ipcdev.pdev)                /* We support only one SCU */
                return -EBUSY;
 
+       pid = id->driver_data;
+       pdata = &intel_scu_ipc_pdata[pid];
+
        ipcdev.pdev = pci_dev_get(dev);
+       ipcdev.irq_mode = pdata->irq_mode;
 
        err = pci_enable_device(dev);
        if (err)
@@ -524,14 +608,16 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
        if (!pci_resource)
                return -ENOMEM;
 
+       init_completion(&ipcdev.cmd_complete);
+
        if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
                return -EBUSY;
 
-       ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR);
+       ipcdev.ipc_base = ioremap_nocache(pdata->ipc_base, pdata->ipc_len);
        if (!ipcdev.ipc_base)
                return -ENOMEM;
 
-       ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR);
+       ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
        if (!ipcdev.i2c_base) {
                iounmap(ipcdev.ipc_base);
                return -ENOMEM;
@@ -564,7 +650,10 @@ static void ipc_remove(struct pci_dev *pdev)
 }
 
 static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
-       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)},
+       {PCI_VDEVICE(INTEL, 0x082a), SCU_IPC_LINCROFT},
+       {PCI_VDEVICE(INTEL, 0x080e), SCU_IPC_PENWELL},
+       {PCI_VDEVICE(INTEL, 0x08ea), SCU_IPC_CLOVERVIEW},
+       {PCI_VDEVICE(INTEL, 0x11a0), SCU_IPC_TANGIER},
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, pci_ids);
index 10d12b221601ddae853fe3b0e8554d99d0c5ef06..3008fd20572e96a2d93823c4d15db27592fed2e2 100644 (file)
@@ -490,11 +490,8 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc)
        int error;
 
        input_dev = input_allocate_device();
-       if (!input_dev) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Couldn't allocate input device for hotkey"));
+       if (!input_dev)
                return -ENOMEM;
-       }
 
        input_dev->name = ACPI_PCC_DRIVER_NAME;
        input_dev->phys = ACPI_PCC_INPUT_PHYS;
index 47caab0ea7a14faa1051b35eafe1ff8e53703ee0..fb233ae7bb0e3e8374691818cfe61125e66371a5 100644 (file)
@@ -140,12 +140,12 @@ MODULE_PARM_DESC(kbd_backlight_timeout,
                 "on the model (default: no change from current value)");
 
 #ifdef CONFIG_PM_SLEEP
-static void sony_nc_kbd_backlight_resume(void);
 static void sony_nc_thermal_resume(void);
 #endif
 static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
                unsigned int handle);
-static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd);
+static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
+               unsigned int handle);
 
 static int sony_nc_battery_care_setup(struct platform_device *pd,
                unsigned int handle);
@@ -304,8 +304,8 @@ static int sony_laptop_input_keycode_map[] = {
        KEY_FN_F10,     /* 14 SONYPI_EVENT_FNKEY_F10 */
        KEY_FN_F11,     /* 15 SONYPI_EVENT_FNKEY_F11 */
        KEY_FN_F12,     /* 16 SONYPI_EVENT_FNKEY_F12 */
-       KEY_FN_F1,      /* 17 SONYPI_EVENT_FNKEY_1 */
-       KEY_FN_F2,      /* 18 SONYPI_EVENT_FNKEY_2 */
+       KEY_FN_1,       /* 17 SONYPI_EVENT_FNKEY_1 */
+       KEY_FN_2,       /* 18 SONYPI_EVENT_FNKEY_2 */
        KEY_FN_D,       /* 19 SONYPI_EVENT_FNKEY_D */
        KEY_FN_E,       /* 20 SONYPI_EVENT_FNKEY_E */
        KEY_FN_F,       /* 21 SONYPI_EVENT_FNKEY_F */
@@ -1444,7 +1444,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
                case 0x014b:
                case 0x014c:
                case 0x0163:
-                       sony_nc_kbd_backlight_cleanup(pd);
+                       sony_nc_kbd_backlight_cleanup(pd, handle);
                        break;
                default:
                        continue;
@@ -1486,13 +1486,6 @@ static void sony_nc_function_resume(void)
                case 0x0135:
                        sony_nc_rfkill_update();
                        break;
-               case 0x0137:
-               case 0x0143:
-               case 0x014b:
-               case 0x014c:
-               case 0x0163:
-                       sony_nc_kbd_backlight_resume();
-                       break;
                default:
                        continue;
                }
@@ -1822,6 +1815,12 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
        int result;
        int ret = 0;
 
+       if (kbdbl_ctl) {
+               pr_warn("handle 0x%.4x: keyboard backlight setup already done for 0x%.4x\n",
+                               handle, kbdbl_ctl->handle);
+               return -EBUSY;
+       }
+
        /* verify the kbd backlight presence, these handles are not used for
         * keyboard backlight only
         */
@@ -1881,9 +1880,10 @@ outkzalloc:
        return ret;
 }
 
-static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
+static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
+               unsigned int handle)
 {
-       if (kbdbl_ctl) {
+       if (kbdbl_ctl && handle == kbdbl_ctl->handle) {
                device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
                device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
                kfree(kbdbl_ctl);
@@ -1891,25 +1891,6 @@ static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
        }
 }
 
-#ifdef CONFIG_PM_SLEEP
-static void sony_nc_kbd_backlight_resume(void)
-{
-       int ignore = 0;
-
-       if (!kbdbl_ctl)
-               return;
-
-       if (kbdbl_ctl->mode == 0)
-               sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base,
-                               &ignore);
-
-       if (kbdbl_ctl->timeout != 0)
-               sony_call_snc_handle(kbdbl_ctl->handle,
-                               (kbdbl_ctl->base + 0x200) |
-                               (kbdbl_ctl->timeout << 0x10), &ignore);
-}
-#endif
-
 struct battery_care_control {
        struct device_attribute attrs[2];
        unsigned int handle;
index 05e046aa5e314be112b0e165f93fd82c6ab16fe5..58b0274d24cc2968e35e6a9cdad75b1533c35e2d 100644 (file)
@@ -6438,7 +6438,12 @@ static struct ibm_struct brightness_driver_data = {
 #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
 #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
 
-static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */
+#if SNDRV_CARDS <= 32
+#define DEFAULT_ALSA_IDX               ~((1 << (SNDRV_CARDS - 3)) - 1)
+#else
+#define DEFAULT_ALSA_IDX               ~((1 << (32 - 3)) - 1)
+#endif
+static int alsa_index = DEFAULT_ALSA_IDX; /* last three slots */
 static char *alsa_id = "ThinkPadEC";
 static bool alsa_enable = SNDRV_DEFAULT_ENABLE1;
 
@@ -9163,7 +9168,6 @@ static int __init thinkpad_acpi_module_init(void)
        mutex_init(&tpacpi_inputdev_send_mutex);
        tpacpi_inputdev = input_allocate_device();
        if (!tpacpi_inputdev) {
-               pr_err("unable to allocate input device\n");
                thinkpad_acpi_module_exit();
                return -ENOMEM;
        } else {
index 67897c8740ba58ea3c93cf54b0a2a3e74a1e3a84..e597de05e6c27badfe97ea533ddb2f7a6c41d3b5 100644 (file)
@@ -97,10 +97,8 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
        int error;
 
        input = input_allocate_device();
-       if (!input) {
-               pr_err("Unable to allocate input device\n");
+       if (!input)
                return -ENOMEM;
-       }
 
        input->name = "Topstar Laptop extra buttons";
        input->phys = "topstar/input0";
index 0cfadb65f7c639597abce1d1bcb81ba3a04ff1d3..7fce391818d30a183962b7888a8bfd275b10423c 100644 (file)
@@ -975,10 +975,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
        u32 hci_result;
 
        dev->hotkey_dev = input_allocate_device();
-       if (!dev->hotkey_dev) {
-               pr_info("Unable to register input device\n");
+       if (!dev->hotkey_dev)
                return -ENOMEM;
-       }
 
        dev->hotkey_dev->name = "Toshiba input device";
        dev->hotkey_dev->phys = "toshiba_acpi/input0";
index 62e8c221d01ea10a5f105a0a44b2d2d8277d7878..c2e7b2657aeb31bf5ab50a4f8a8821e2ad53f696 100644 (file)
@@ -672,8 +672,10 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
        struct wmi_block *wblock;
 
        wblock = dev_get_drvdata(dev);
-       if (!wblock)
-               return -ENOMEM;
+       if (!wblock) {
+               strcat(buf, "\n");
+               return strlen(buf);
+       }
 
        wmi_gtoa(wblock->gblock.guid, guid_string);
 
index 747826d99059955f8941d592ef2f7734cd38fd08..14655a0f0431b35bd9a0988b669c510a162155e3 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 e6f92b450913e47c490b3481c4c33c537c2526bb..5e2054afe840e9d6412a28c5250f34d1017aa7b2 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 a4b74177706f24397f327ee4361c7408bf6b0d90..372b4e8ab59863a89b8de900d5e2de492823dee8 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 a4c4a10b3a41cd2e2e05d67773097cbfed686f8f..19110aa613a13110ba08d8c7cc783ba834f990b7 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 754970717c317915ca3f9c5d8a2008e48bb79554..3cb4178e397c014695d7985383fd806b1ae8b79e 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 0727f9256138589cf0184756b46adc5f216df4bd..df893dd1447d1534695ace9405e4783ec49fd79d 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 e30e847600bb69c71519b7e1325286830e504c53..7287c0efd6bfc5e2a877d322c82cf4dae0e68b7a 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 fc04d191579bd987ba0ad94ddd6b298c329c2b18..1bb3a91b1acc2eb376d6460006218a7b1f4e1e76 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 d9686aa9270a0b2732292e75b38c57ea32d1c064..6c8931d4ad62a554e3f185af3ada317ac61ffc7d 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 d664ef58afa7d0ffd34d03e89444cd015f31ab7d..e0b22f9b6fdd643edd124d3940025636924d0dc3 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 ffa10ed83eb1559481c0f1252f37644013c0b9b1..62c15af58c9af37420ab81e889b6f3d61021086d 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 bdd7b9b2546a500bc7366cfea79f5b1970cc0fae..8fc9d6df87f682610187a7ca665c393ef30c6e1d 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 d98abe911e376cc69f187c53caae17a979d812d8..f14108844e1a100b8ae61d6bbd9f7124653218f0 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 75840b5cea6dfd978d83c5b4c741af0b5af99122..eece329d78729049c4dd7b2e49668217309a5668 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 77a8c185c5b2b46c6a4afe0e0a45bc8d5dd772d0..8b754e4dba4aae03cc36562d5e0c079e6327c4d2 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 ba6ce01035e4feae2c1f40dcf8b24e0ad4923ffa..f3dcd02390f1b5da7d9cf43d3b053d3e6840bc0f 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 2b7c4f88b461b788c1d683a141d6a402d958af12..cc477334487494fe014ba8993feb8c674fadad3f 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 efac99e03d57191c2d8667264cff0a5e8a603dbe..9dc0f9d42bfa3a44807c8f2b3917ad27c3f8f437 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 c2c5a4fd1b96bb4eede5bdd553911e3da66dee5e..9475bc7a6f97ded7ec293881151747073c198ff3 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 fcc8b9adde9fe89b2e35ae3ae01312aaff90605c..b59639e0c02978388126a1d7d56d8f3b62c32b6c 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 c2e2e5852362afbd62a8c097fb195ea92433cf15..4e5c3d13d4f8d31a0faa65ccd7146c0a28436e3c 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 084f552465322654f9f3b7e0a765926ff8bd49fd..a4d8f519d965660d1a33e0cdfc7bda02224fbd7d 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 29d1bba4804ec95fa7c249ebbe6ad3ea057fe08a..b964470025c5e288a6009817ddb683c679868eb6 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 eef910580eaef3c355bb0384759b16155924e9d8..b99a50e626a64f82ca98783cc373bd2bc4c82e01 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 724706a97dc40c5cf7e21d89f7c58bc2385f6226..fd3154d86901fcf3865139d42bc997d72b4ff608 100644 (file)
@@ -174,6 +174,33 @@ static const struct regulator_desc arizona_micsupp = {
        .owner = THIS_MODULE,
 };
 
+static const struct regulator_linear_range arizona_micsupp_ext_ranges[] = {
+       REGULATOR_LINEAR_RANGE(900000,  0,    0x14, 25000),
+       REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
+};
+
+static const struct regulator_desc arizona_micsupp_ext = {
+       .name = "MICVDD",
+       .supply_name = "CPVDD",
+       .type = REGULATOR_VOLTAGE,
+       .n_voltages = 40,
+       .ops = &arizona_micsupp_ops,
+
+       .vsel_reg = ARIZONA_LDO2_CONTROL_1,
+       .vsel_mask = ARIZONA_LDO2_VSEL_MASK,
+       .enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
+       .enable_mask = ARIZONA_CPMIC_ENA,
+       .bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
+       .bypass_mask = ARIZONA_CPMIC_BYPASS,
+
+       .linear_ranges = arizona_micsupp_ext_ranges,
+       .n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges),
+
+       .enable_time = 3000,
+
+       .owner = THIS_MODULE,
+};
+
 static const struct regulator_init_data arizona_micsupp_default = {
        .constraints = {
                .valid_ops_mask = REGULATOR_CHANGE_STATUS |
@@ -186,9 +213,22 @@ static const struct regulator_init_data arizona_micsupp_default = {
        .num_consumer_supplies = 1,
 };
 
+static const struct regulator_init_data arizona_micsupp_ext_default = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+                               REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_BYPASS,
+               .min_uV = 900000,
+               .max_uV = 3300000,
+       },
+
+       .num_consumer_supplies = 1,
+};
+
 static int arizona_micsupp_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+       const struct regulator_desc *desc;
        struct regulator_config config = { };
        struct arizona_micsupp *micsupp;
        int ret;
@@ -207,7 +247,17 @@ static int arizona_micsupp_probe(struct platform_device *pdev)
         * default init_data for it.  This will be overridden with
         * platform data if provided.
         */
-       micsupp->init_data = arizona_micsupp_default;
+       switch (arizona->type) {
+       case WM5110:
+               desc = &arizona_micsupp_ext;
+               micsupp->init_data = arizona_micsupp_ext_default;
+               break;
+       default:
+               desc = &arizona_micsupp;
+               micsupp->init_data = arizona_micsupp_default;
+               break;
+       }
+
        micsupp->init_data.consumer_supplies = &micsupp->supply;
        micsupp->supply.supply = "MICVDD";
        micsupp->supply.dev_name = dev_name(arizona->dev);
@@ -226,7 +276,7 @@ static int arizona_micsupp_probe(struct platform_device *pdev)
                           ARIZONA_CPMIC_BYPASS, 0);
 
        micsupp->regulator = devm_regulator_register(&pdev->dev,
-                                                    &arizona_micsupp,
+                                                    desc,
                                                     &config);
        if (IS_ERR(micsupp->regulator)) {
                ret = PTR_ERR(micsupp->regulator);
index 6382f0af353bc257e3ee6c3b545f75c0c1f2904e..3fe13130baec12218a58230863b313bf4c20d034 100644 (file)
@@ -2184,6 +2184,9 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector)
        struct regulator_ops    *ops = rdev->desc->ops;
        int                     ret;
 
+       if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1 && !selector)
+               return rdev->desc->fixed_uV;
+
        if (!ops->list_voltage || selector >= rdev->desc->n_voltages)
                return -EINVAL;
 
index 04406a918c041b5773adcf9adf331eadf289caf4..234960dc96077389460632cdc832f0c6ea5e69e1 100644 (file)
@@ -139,6 +139,7 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np)
        struct property *prop;
        const char *regtype;
        int proplen, gpio, i;
+       int ret;
 
        config = devm_kzalloc(dev,
                        sizeof(struct gpio_regulator_config),
@@ -202,7 +203,11 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np)
        }
        config->nr_states = i;
 
-       of_property_read_string(np, "regulator-type", &regtype);
+       ret = of_property_read_string(np, "regulator-type", &regtype);
+       if (ret < 0) {
+               dev_err(dev, "Missing 'regulator-type' property\n");
+               return ERR_PTR(-EINVAL);
+       }
 
        if (!strncmp("voltage", regtype, 7))
                config->type = REGULATOR_VOLTAGE;
index ba67b2c4e2e7fe4da91fdd6afd269ab1233a284e..032df3799efb7a144f6c1eef5cd0a3dfe17b6e50 100644 (file)
@@ -308,9 +308,15 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip)
        if (ret)
                return ret;
 
-       if (value & 0x0f) {
-               dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
-               return -ENODEV;
+       switch (value & 0x0f) {
+               /* Freescale misprogrammed 1-3% of parts prior to week 8 of 2013 as ID=8 */
+               case 0x8:
+                       dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8");
+               case 0x0:
+                       break;
+               default:
+                       dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
+                       return -ENODEV;
        }
 
        ret = regmap_read(pfuze_chip->regmap, PFUZE100_REVID, &value);
index 23f8d1ce877d4e133fc690b7055b4342841e4112..a00132e31ec7ab245f2017f13e110c6730c906fd 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 b09c75c21b609c51569e4088d8aba51769bec8a4..a34b50690b4efd28a3616d98f304185b723233b8 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 15f166a470a7f3fe5eb0e45dd2be80c2a384a76d..0077302221164e62df209626e6595a5a9505e4ee 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 8b2cd8a5a2ffe3d5928d2bc7f026f3fcceaa1dca..c0da95e95702123d403bf58ed4b894a104eeb2ec 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 45560ffb038d21e5ca5e7cf98afad3fdde634b29..965a9da70867fc95be934283fad8ff274ec21807 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 cee7e2708a1fe35359eb81cc458d939e50ad1906..95e45782692fa7bb2a89e9ec566a6a035f736381 100644 (file)
@@ -3224,6 +3224,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
 
        fcx_multitrack = private->features.feature[40] & 0x20;
        data_size = blk_rq_bytes(req);
+       if (data_size % blksize)
+               return ERR_PTR(-EINVAL);
        /* tpm write request add CBC data on each track boundary */
        if (rq_data_dir(req) == WRITE)
                data_size += (last_trk - first_trk) * 4;
index 548209a9c43c58ae687809d5da605f7b8e97184b..d0ab5019d885cea6113f677a4f58990fa4a3ca55 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 c0d102e3a48bf569d0c9744abb466317e942342b..27f930cd657fcdd3a223412cb68c393ebd565556 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 17821a026c9ca9b4ae6878bb57c04a56692850e4..b69ab17f13fabef4afc6c44a62534d549505db12 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 f93cc32eb81871b468c3439ecb03de5c19783710..71e9747380149d627a92883b2d17bd269c9f7817 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 40d1406289ed2533f6bd4bdffda0b0e19a80a79e..6fbe09686d18fc1024fb3f1151914fc69abaa394 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 77df9cb00688feeda6cad1fbfd4623fc5a26134e..eaa21d542c5cb102c19753eeec8abd2fff719cf0 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 b1032931a1c41237766eaf6c38d7e5032e71e33d..561a0414b35282f97174d0cfb0e68ce87fab9c50 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 ffb1fcf0bf5bed9928f53e1205f8b6b01635f6b6..3d8e4d63f51418c876f9918e0be77be628d2afb3 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 aca7bfc113aaeb4067043cd10bdc0662dd8ec286..3a2ee4a740b4465da4bd80948bd5daa57e703cae 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 46ec25632e8bd4ef8e0be26a3f515697abd7f760..15268edc54aea979c581e9135bb3c325b0d31dde 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 af2166fa515953ff8beaf57226182095c67f9654..1abd0db29915bf69b8618b7c267dad552573a74d 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 779dc5136291610f28361b7386151cc1bb4b9372..d6297176ab85b8b306eb69be685dfba811ae35ba 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 132a905b6bdb76e5012399eda5df32e04bbefa81..0ca64484cfa3328bd33d5e5b0cb028e7d4991424 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 d85ac1a9d2c0698b85c606ab6b2ff917cb0959ed..fbcd48d0bfc3397f2a5b202b093d06bbaa2420a6 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 33c52bc2c7b461033b8845e7e538f072f9ab671f..97fd450aff09315194233e7a8ea1f06b566248ef 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 15a629d8ed081efe1e5b9e13160703c3d2f8fe6e..a795d81ef8750881aa55cf47c5558a5cdb7da175 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 7591fa4e28bb2be8ed6e6a64e972568429457f20..fc80a325a1e6577c8fb5225d9f8a7ea614311540 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 5be718c241c4f329edb1c23e684daa7b4681a0f1..e4cf23df4b4f094d64a40332c81facd39da4097c 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 fabeb88602acb65c8b5b7e493314f30822d0227e..854dad7d5b03318d10077d2e367eeb6ca8242dc6 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 00346fe939d554b5d089980718cd2fac1492ba41..1aafc331ee63a1a2d9380ffd53710da3466bbf96 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 42e8624a9b9a1137fe77088e1efa3f3b0186fa7f..83d9bf6fa6ca9989717e0fd9f4ae20b9b8d7e724 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 be09b101b4a148534caafb39071ee7827904e648..33e4ec2bfe734eefda1cd049bfde7f1112cf0ea2 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 6d55b4e7e7923121d58535ab1a7ff83c50298c8c..ee4fa40a50b131597a3e1b6960bffc17191f2d03 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 fb5a8981515057b8bbffe5423a41afbe9bc7e71d..22f6432eb4755a20af732e8c4d4060e9fbb9e909 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 ca6bf2af7ce8b7b0fb8a657f1f511f33f44f5028..68c94cc85c35da9dd6e4ebab0052867f5c07c01f 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 515c9629e9fedf0679257a762b553045858070c1..d1a4b82836ea6936f55504e33bcaabb5db9adbee 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 2cf9470dd11b55a0840f8d31f9a10c257c1c56d5..0a743a5d16477a5e168f02a77a99e90f201caac3 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 7b7381d7671fdc9cfeecdf3c256984b0261d3116..5ff978be249d22b37df66601787cceca92933047 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 6b1b4e91e53f655128edf0235589bd02081963fb..6c1f223a8e1d335fa7c86a374e470e666e848906 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 c3601b57a80c796cd32aebeb3c00f3d94e89b06f..edbee8dc62c9ab546b5340ca06cbef62ba4cecf3 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 5982a587babc875a56aaba6ed9983860c8b593a9..7d014b11df62fc086be5d8deff46ad4facaa4499 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 662bf13c42f012720f114cdf6d4355a0446c5280..34f5f5ffef056ec2e86e44726dd99c13ae204877 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 1eb7b0280a45447fc1855ce0a653488cf1591002..bd6f743d87a78af19c698d38d70291723fd28dfb 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 bcd57f699ebbcc7dad7d597044c7bc54da22cf52..52be35e0300c901ac18faff6ded2aaf65e43b675 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 f85b9e5c1f059dca0d62399ac52ecfe031dfbae0..7eb19be35d461cfff2cc9f27897fd8b91bf8d17b 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 329327528a55b508711b79da5ead5b86817108ca..771f7b816443603bb1787a93f4aa79666bdf5f69 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 6dc3e99b7f9cd22e23aa0df57e223a3ed6a0227f..a28d5e624aabcdd729e4c64c4c904549e21d6e17 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 f379c7f3034cd55392355189e081b45547323008..2700a5a09bd45a103b857b2f6a81da42834e4d28 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);
 }
@@ -184,6 +672,24 @@ static int srp_host_match(struct attribute_container *cont, struct device *dev)
        return &i->t.host_attrs.ac == cont;
 }
 
+/**
+ * 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.
@@ -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 325c31caa6e0dd98dbce0724b968a7ee5efcba69..1aa4befcfbd0120b11df14a4b0f918b00f5be72d 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 b80bf709f104ade623662ea8c646dea308a67818..805369521df8eeb22de129d70432444b45269029 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 11423615c2eaa8b8ea84a097e632204ca6d2503b..b006cf789ba1186ac3bac5a69c9ca71a17da8837 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 a823cf44e9494bdc506a2e4b86d7edb190ea76dc..8b9531204c2bb7ee570fb3d504a59803765a4ba6 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 74b88efde6ad408123de6a9b96ec37460195fe82..c3173dced87038f41b00d918d1f114ceaac09208 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 3bfaa66fa0d16d31610ee33ef41c9cce10031f1a..b9755ec0e8123cdd7a3479659fb2111924dc3056 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 4c332143a3100c52d916fc9887ffd11d96356892..3ed666fe840a0cdbaf1a2a2c6cd75670dd25fb48 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 e2a5a426b2efc57038503ed7b64d00237e190b14..6f03d7e6435d82d367d568bd478af2aed9394a98 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 dd72445ba2ea2d14f6afd7fb53c5317c1aee81af..50b2d88c81901c2b0b0ec1720f7e802ccf4924fb 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 b9f0192758d6d929aab86d087c443adc46154e66..6d207afec8cbdb578c9e5428d6018dff1d93690b 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 32200d4f8780d4f6372e0ba0ce8388873a23d304..80d8f40f7e0553d600f1e36b018daa7fae275a64 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 2129fcd1c31b6513319debe93978efd1b1c197ea..119f7af945374f43a7e1547efbb2f5aa1d9517b0 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 58d5ee0e4443e24442cb4cbeb51b2e80ac637d3b..9602bbd8d7eac061fffb5543704fc9d68a48f135 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 de333059a9a7ec0f55c134f15161c55bd414648c..73afb56c08cc26826d468ad4aaacdce3a7e7043f 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 9e2020df9e0f7e5c473cf500b82c611d4625686f..4c4b0a1219a715322d331b23232f8e92e61f92dc 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 2a95435a6a11d1b554b9a82e8f04c19a39ed0872..c74298cf70e2406d8972f4fe75cc2fcd59a15864 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 592b4aff651f674c39f5c136a7bab01f9b178898..ed5e501c465276b6d39318818fa5d668d2685668 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 9146bb3c24895803904bf67281b38ce9bc9c8395..aaecfb3ebf580bd9f746458b26175da61f1bc994 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 79be8ce6a9d11368389c97faa0047c8ab5b5d8e1..4dc8e8129459b3eaa4bb18744e33dcbd578b0da2 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 af0a67886ae8bee523c469af2d89d49aca9c2ec5..e66715ba37ed680811d7a53eb08e1b66979994c7 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 ec3a83f52ea2faead4cb43ab9908c087004469e9..6d4ce4615163ddb8efbdfc798cec36341b6baa1b 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 927998aa5e71e711177a3adbf7cfe94ed020a85b..18cc625d887f796aed4b082eb2366262aec8aaa8 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 7a9bf3b578104bf57939ce032c96dc17c88840be..9a5ebd6cc51235b699756c1c08c7609235b7d1e4 100644 (file)
@@ -1284,9 +1284,8 @@ done:
        kfree_skb(skb);
 }
 
-static int btmtk_usb_send_frame(struct sk_buff *skb)
+static int btmtk_usb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hci_dev *hdev = (struct hci_dev *)skb->dev;
        struct btmtk_usb_data *data = hci_get_drvdata(hdev);
        struct usb_ctrlrequest *dr;
        struct urb *urb;
index d041b714db29732f91e8739db8dcf1122f55d55b..2baaf1db6fbf34e6a9d8694d560b6a047871bf81 100644 (file)
@@ -173,11 +173,11 @@ static int pcl730_do_insn_bits(struct comedi_device *dev,
        if (mask) {
                if (mask & 0x00ff)
                        outb(s->state & 0xff, dev->iobase + reg);
-               if ((mask & 0xff00) & (s->n_chan > 8))
+               if ((mask & 0xff00) && (s->n_chan > 8))
                        outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
-               if ((mask & 0xff0000) & (s->n_chan > 16))
+               if ((mask & 0xff0000) && (s->n_chan > 16))
                        outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
-               if ((mask & 0xff000000) & (s->n_chan > 24))
+               if ((mask & 0xff000000) && (s->n_chan > 24))
                        outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
        }
 
index 6815cfe2664e42e632114b3e87ca140b349008f3..b486099b543d56e61a14e94b3bc698c58cc04cb0 100644 (file)
@@ -494,7 +494,7 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val)
  * Private helper function: Write setpoint to an application DAC channel.
  */
 static void s626_set_dac(struct comedi_device *dev, uint16_t chan,
-                        unsigned short dacdata)
+                        int16_t dacdata)
 {
        struct s626_private *devpriv = dev->private;
        uint16_t signmask;
index 933b01a0f03d4274e4f82ced6240a79f78b2c742..0adf3cffddb07251f09d961eedc490206db88fb3 100644 (file)
@@ -465,7 +465,7 @@ static int vmk80xx_do_insn_bits(struct comedi_device *dev,
        unsigned char *rx_buf = devpriv->usb_rx_buf;
        unsigned char *tx_buf = devpriv->usb_tx_buf;
        int reg, cmd;
-       int ret;
+       int ret = 0;
 
        if (devpriv->model == VMK8061_MODEL) {
                reg = VMK8061_DO_REG;
index 68ded17c0f5c7f9302613d37ed48238f9f54422b..12f333fa59b525ef7598d2a9cf0e213f925b34a4 100644 (file)
@@ -578,7 +578,7 @@ static int request_code_segment(struct ft1000_usb *ft1000dev, u16 **s_file,
                 u8 **c_file, const u8 *endpoint, bool boot_case)
 {
        long word_length;
-       int status;
+       int status = 0;
 
        /*DEBUG("FT1000:REQUEST_CODE_SEGMENT\n");i*/
        word_length = get_request_value(ft1000dev);
@@ -1074,4 +1074,3 @@ int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart,
 
        return status;
 }
-
index 9221a74efd18a00cf31c24c79b3e271a7a181d85..93c7299e83539c2e38edb4d3b10e44867c2333dc 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 aeae76b77be5732eebd2756a8c07353f99d4be0c..e2dd7830b3204a58541a8ec7e8fed8c68ca93bbd 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 a3ea69e9d800ef3ae992efbacb4575bc7ad94782..34634da1f9f733fd0268a728fc5e3653aeedff58 100644 (file)
@@ -6,6 +6,8 @@ menu "Magnetometer sensors"
 config SENSORS_HMC5843
        tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer"
        depends on I2C
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
        help
          Say Y here to add support for the Honeywell HMC5843, HMC5883 and
          HMC5883L 3-Axis Magnetometer (digital compass).
index 394254f7d6b545cf0dfdff0b9e77f2f98042e8f3..5032ff7c2259a8b1e9308db04324fee929249487 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 2c3a9e178fb5e65a073dc777b1c7dd86c7a9f34f..8742432d7b0170f72144675d8d9b591f976a0af7 100644 (file)
@@ -8,4 +8,6 @@ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
 obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
 obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
-obj-$(CONFIG_DRM_IMX_IPUV3)    += ipuv3-crtc.o ipuv3-plane.o
+
+imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
+obj-$(CONFIG_DRM_IMX_IPUV3)    += imx-ipuv3-crtc.o
index 3d3a824f6de7d412964571349fe507b27ca59beb..6bd015ac9d683474a034924f8ffec3e752e1d382 100644 (file)
@@ -72,6 +72,7 @@ int imx_drm_crtc_id(struct imx_drm_crtc *crtc)
 {
        return crtc->pipe;
 }
+EXPORT_SYMBOL_GPL(imx_drm_crtc_id);
 
 static void imx_drm_driver_lastclose(struct drm_device *drm)
 {
@@ -407,14 +408,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 +435,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 5dec771d70eee8c08a6bc0b787f7f116ddd6906d..4d340f4a2198618d22bc37ab9cebb4f0c0cb2c06 100644 (file)
@@ -409,8 +409,8 @@ int ptlrpc_stop_pinger(void)
        struct l_wait_info lwi = { 0 };
        int rc = 0;
 
-       if (!thread_is_init(&pinger_thread) &&
-           !thread_is_stopped(&pinger_thread))
+       if (thread_is_init(&pinger_thread) ||
+           thread_is_stopped(&pinger_thread))
                return -EALREADY;
 
        ptlrpc_pinger_remove_timeouts();
index 58684da45e6c2927cb042ceb0dc4a364b3037a5c..b658c2316df340b4480ed72ca90d457d913e3691 100644 (file)
@@ -15,6 +15,8 @@
  * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -661,7 +663,7 @@ static int go7007_usb_interface_reset(struct go7007 *go)
 
        if (usb->board->flags & GO7007_USB_EZUSB) {
                /* Reset buffer in EZ-USB */
-               dev_dbg(go->dev, "resetting EZ-USB buffers\n");
+               pr_debug("resetting EZ-USB buffers\n");
                if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 ||
                    go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0)
                        return -1;
@@ -689,7 +691,7 @@ static int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
        u16 status_reg = 0;
        int timeout = 500;
 
-       dev_dbg(go->dev, "WriteInterrupt: %04x %04x\n", addr, data);
+       pr_debug("WriteInterrupt: %04x %04x\n", addr, data);
 
        for (i = 0; i < 100; ++i) {
                r = usb_control_msg(usb->usbdev,
@@ -734,7 +736,7 @@ static int go7007_usb_onboard_write_interrupt(struct go7007 *go,
        int r;
        int timeout = 500;
 
-       dev_dbg(go->dev, "WriteInterrupt: %04x %04x\n", addr, data);
+       pr_debug("WriteInterrupt: %04x %04x\n", addr, data);
 
        go->usb_buf[0] = data & 0xff;
        go->usb_buf[1] = data >> 8;
@@ -771,7 +773,7 @@ static void go7007_usb_readinterrupt_complete(struct urb *urb)
                go->interrupt_available = 1;
                go->interrupt_data = __le16_to_cpu(regs[0]);
                go->interrupt_value = __le16_to_cpu(regs[1]);
-               dev_dbg(go->dev, "ReadInterrupt: %04x %04x\n",
+               pr_debug("ReadInterrupt: %04x %04x\n",
                                go->interrupt_value, go->interrupt_data);
        }
 
@@ -891,7 +893,7 @@ static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len)
        int transferred, pipe;
        int timeout = 500;
 
-       dev_dbg(go->dev, "DownloadBuffer sending %d bytes\n", len);
+       pr_debug("DownloadBuffer sending %d bytes\n", len);
 
        if (usb->board->flags & GO7007_USB_EZUSB)
                pipe = usb_sndbulkpipe(usb->usbdev, 2);
@@ -977,7 +979,7 @@ static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
                                !(msgs[i].flags & I2C_M_RD) &&
                                (msgs[i + 1].flags & I2C_M_RD)) {
 #ifdef GO7007_I2C_DEBUG
-                       dev_dbg(go->dev, "i2c write/read %d/%d bytes on %02x\n",
+                       pr_debug("i2c write/read %d/%d bytes on %02x\n",
                                msgs[i].len, msgs[i + 1].len, msgs[i].addr);
 #endif
                        buf[0] = 0x01;
@@ -988,7 +990,7 @@ static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
                        buf[buf_len++] = msgs[++i].len;
                } else if (msgs[i].flags & I2C_M_RD) {
 #ifdef GO7007_I2C_DEBUG
-                       dev_dbg(go->dev, "i2c read %d bytes on %02x\n",
+                       pr_debug("i2c read %d bytes on %02x\n",
                                        msgs[i].len, msgs[i].addr);
 #endif
                        buf[0] = 0x01;
@@ -998,7 +1000,7 @@ static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
                        buf_len = 4;
                } else {
 #ifdef GO7007_I2C_DEBUG
-                       dev_dbg(go->dev, "i2c write %d bytes on %02x\n",
+                       pr_debug("i2c write %d bytes on %02x\n",
                                        msgs[i].len, msgs[i].addr);
 #endif
                        buf[0] = 0x00;
@@ -1057,7 +1059,7 @@ static int go7007_usb_probe(struct usb_interface *intf,
        char *name;
        int video_pipe, i, v_urb_len;
 
-       dev_dbg(go->dev, "probing new GO7007 USB board\n");
+       pr_debug("probing new GO7007 USB board\n");
 
        switch (id->driver_info) {
        case GO7007_BOARDID_MATRIX_II:
@@ -1097,13 +1099,13 @@ static int go7007_usb_probe(struct usb_interface *intf,
                board = &board_px_tv402u;
                break;
        case GO7007_BOARDID_LIFEVIEW_LR192:
-               dev_err(go->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n");
+               dev_err(&intf->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n");
                return -ENODEV;
                name = "Lifeview TV Walker Ultra";
                board = &board_lifeview_lr192;
                break;
        case GO7007_BOARDID_SENSORAY_2250:
-               dev_info(go->dev, "Sensoray 2250 found\n");
+               dev_info(&intf->dev, "Sensoray 2250 found\n");
                name = "Sensoray 2250/2251";
                board = &board_sensoray_2250;
                break;
@@ -1112,7 +1114,7 @@ static int go7007_usb_probe(struct usb_interface *intf,
                board = &board_ads_usbav_709;
                break;
        default:
-               dev_err(go->dev, "unknown board ID %d!\n",
+               dev_err(&intf->dev, "unknown board ID %d!\n",
                                (unsigned int)id->driver_info);
                return -ENODEV;
        }
@@ -1247,7 +1249,7 @@ static int go7007_usb_probe(struct usb_interface *intf,
                                        sizeof(go->name));
                        break;
                default:
-                       dev_dbg(go->dev, "unable to detect tuner type!\n");
+                       pr_debug("unable to detect tuner type!\n");
                        break;
                }
                /* Configure tuner mode selection inputs connected
index b6cb593f55c6727046903828886dfe453530a00c..cbea5d84fed387bb00183d4cf217f2fbebc09739 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 fbbdce4c119f71cab122bde5c203bb03b9ee43de..30edc740ac2546fcaea7b0dfcc82ec67c1eb7f06 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 f6bc4c91ab35f4737338e945a70b7a7fedf84f4c..2e3a98575d47f9fbdddebecdd2a17d9741ffd09e 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 11d5338b4f2ffaa458ddc6729a5ea48298e17227..0feeaadf29dc12c47eee290c7616cfaf915e6ace 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 76d5bbd4d93c38f3e25274f8d7b337eb808f1b7d..0c349c8595e44d542c4625de893cc709ca1acfe7 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 32d9953bc36eb00ad0ec31bf2c8c5fbe77ad354b..145295a5db72baa1942217c4d5dc19023a278132 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 333594189b81e74e8ac83141b55d2d8f8b8737c5..7f2f2472655bf8a0c96eed19d251410c0e53aaf9 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 a4c589604b028d7d74e1a7f895b6ab3aecbeac75..d582c5b84c14cc2c8821d9a982ba47746187cf76 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 6f91d2e34b2a7bd971e8e3d3060b8e98b4f4f566..f1bbb8cb74e69db4fcc2d327c745d7e261f685f6 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 3066ee2e753be3ed887d11b9615b41b78261b6bb..49ea76b3435dcd19b9a9f8bbbfb561a92b0a53fe 100644 (file)
@@ -681,7 +681,8 @@ static irqreturn_t nvec_interrupt(int irq, void *dev)
                        dev_err(nvec->dev,
                                "RX buffer overflow on %p: "
                                "Trying to write byte %u of %u\n",
-                               nvec->rx, nvec->rx->pos, NVEC_MSG_SIZE);
+                               nvec->rx, nvec->rx ? nvec->rx->pos : 0,
+                               NVEC_MSG_SIZE);
                break;
        default:
                nvec->state = 0;
index 2c678f4095734698b2fcfcdf6318691b7b8c4855..2f548ebada59286fbc8290bd3ce8724d2641aae0 100644 (file)
@@ -1115,6 +1115,9 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf,  int len)
                        return _FAIL;
        }
 
+       /* fix bug of flush_cam_entry at STOP AP mode */
+       psta->state |= WIFI_AP_STATE;
+       rtw_indicate_connect(padapter);
        pmlmepriv->cur_network.join_res = true;/* for check if already set beacon */
        return ret;
 }
index 165b918b8171b6380c4d8653d576a0bc085ecb99..1b6d581c438b56a970fdf1b08e52c94846c9d793 100644 (file)
@@ -4,7 +4,7 @@
 
 menuconfig TIDSPBRIDGE
        tristate "DSP Bridge driver"
-       depends on ARCH_OMAP3 && !ARCH_MULTIPLATFORM
+       depends on ARCH_OMAP3 && !ARCH_MULTIPLATFORM && BROKEN
        select MAILBOX
        select OMAP2PLUS_MBOX
        help
index 7bb550acaf4a88a4e3a37e6c07d3eaaa27907d38..743ff09d82d2de8a8f1596d80f23f7a19d794491 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 58a0d5c5543d1617c82a9968c9c479952675892a..fc19b970708783c7d10298c51ddfaec2b8855a4a 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 6d04eb48bfbce9362862d47e28b6d3d1a0b84be0..1aa4a3fd0f1ba3b023367974593897ba1077df18 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 aab0012bba92909611659b78ef982d48aec82cfb..ab8b2ba6eedd79cdb2146ef0911fc0ae88e7a63a 100644 (file)
@@ -143,7 +143,8 @@ static int hostap_disable_hostapd(PSDevice pDevice, int rtnl_locked)
                DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "%s: Netdevice %s unregistered\n",
                        pDevice->dev->name, pDevice->apdev->name);
        }
-       free_netdev(pDevice->apdev);
+       if (pDevice->apdev)
+               free_netdev(pDevice->apdev);
        pDevice->apdev = NULL;
        pDevice->bEnable8021x = false;
        pDevice->bEnableHostWEP = false;
index 1e8b8412e67e4b6663161c977f2334a154b65e0a..4aa5ef54b683734097075127136512f314c5664f 100644 (file)
@@ -939,6 +939,7 @@ int BBbVT3184Init(struct vnt_private *pDevice)
     u8 *                   pbyAgc;
     u16                    wLengthAgc;
     u8                    abyArray[256];
+       u8 data;
 
     ntStatus = CONTROLnsRequestIn(pDevice,
                                   MESSAGE_TYPE_READ,
@@ -1104,6 +1105,16 @@ else {
     ControlvWriteByte(pDevice,MESSAGE_REQUEST_BBREG,0x0D,0x01);
 
     RFbRFTableDownload(pDevice);
+
+       /* Fix for TX USB resets from vendors driver */
+       CONTROLnsRequestIn(pDevice, MESSAGE_TYPE_READ, USB_REG4,
+               MESSAGE_REQUEST_MEM, sizeof(data), &data);
+
+       data |= 0x2;
+
+       CONTROLnsRequestOut(pDevice, MESSAGE_TYPE_WRITE, USB_REG4,
+               MESSAGE_REQUEST_MEM, sizeof(data), &data);
+
     return true;//ntStatus;
 }
 
index ae1676d190c5b318fcaa43466f58539d39fed095..67ba48b9a8d906d7c8e1091a7ccfdddebf9b9ee5 100644 (file)
@@ -133,7 +133,8 @@ static int hostap_disable_hostapd(struct vnt_private *pDevice, int rtnl_locked)
             DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "%s: Netdevice %s unregistered\n",
                       pDevice->dev->name, pDevice->apdev->name);
        }
-       free_netdev(pDevice->apdev);
+       if (pDevice->apdev)
+               free_netdev(pDevice->apdev);
        pDevice->apdev = NULL;
     pDevice->bEnable8021x = false;
     pDevice->bEnableHostWEP = false;
index 5e073062017a2e657e1f0b33ebddcaa1d2fa21c4..5cf5e732a36fdc1f4a485c14b3eaa05c14a1f0c5 100644 (file)
@@ -66,6 +66,8 @@
 
 #define VIAUSB20_PACKET_HEADER          0x04
 
+#define USB_REG4       0x604
+
 typedef struct _CMD_MESSAGE
 {
     u8        byData[256];
index 79ce363b2ea9d1dd2cb0b79362b607659ca37345..3277d9838f4e928ab3555720a186e476e826a720 100644 (file)
@@ -652,21 +652,30 @@ static ssize_t reset_store(struct device *dev,
                return -ENOMEM;
 
        /* Do not reset an active device! */
-       if (bdev->bd_holders)
-               return -EBUSY;
+       if (bdev->bd_holders) {
+               ret = -EBUSY;
+               goto out;
+       }
 
        ret = kstrtou16(buf, 10, &do_reset);
        if (ret)
-               return ret;
+               goto out;
 
-       if (!do_reset)
-               return -EINVAL;
+       if (!do_reset) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        /* Make sure all pending I/O is finished */
        fsync_bdev(bdev);
+       bdput(bdev);
 
        zram_reset_device(zram, true);
        return len;
+
+out:
+       bdput(bdev);
+       return ret;
 }
 
 static void __zram_make_request(struct zram *zram, struct bio *bio, int rw)
index 1a67537dbc5654be28e7d640f89d3fe3fd903e30..3b950e5a918f8c1a252aabc7da049e2732e2e15d 100644 (file)
@@ -430,7 +430,12 @@ static struct page *get_next_page(struct page *page)
        return next;
 }
 
-/* Encode <page, obj_idx> as a single handle value */
+/*
+ * Encode <page, obj_idx> as a single handle value.
+ * On hardware platforms with physical memory starting at 0x0 the pfn
+ * could be 0 so we ensure that the handle will never be 0 by adjusting the
+ * encoded obj_idx value before encoding.
+ */
 static void *obj_location_to_handle(struct page *page, unsigned long obj_idx)
 {
        unsigned long handle;
@@ -441,17 +446,21 @@ static void *obj_location_to_handle(struct page *page, unsigned long obj_idx)
        }
 
        handle = page_to_pfn(page) << OBJ_INDEX_BITS;
-       handle |= (obj_idx & OBJ_INDEX_MASK);
+       handle |= ((obj_idx + 1) & OBJ_INDEX_MASK);
 
        return (void *)handle;
 }
 
-/* Decode <page, obj_idx> pair from the given object handle */
+/*
+ * Decode <page, obj_idx> pair from the given object handle. We adjust the
+ * decoded obj_idx back to its original value since it was adjusted in
+ * obj_location_to_handle().
+ */
 static void obj_handle_to_location(unsigned long handle, struct page **page,
                                unsigned long *obj_idx)
 {
        *page = pfn_to_page(handle >> OBJ_INDEX_BITS);
-       *obj_idx = handle & OBJ_INDEX_MASK;
+       *obj_idx = (handle & OBJ_INDEX_MASK) - 1;
 }
 
 static unsigned long obj_idx_to_offset(struct page *page,
index 38e44b9abf0f145eacde1e9b90dcf26c45ca2633..d70e9119e906cba0c0f38da61948ce745bc5d6e5 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 7505fddca15f639b0a40fde300a2ca19c19d37bd..de77d9aa22c6329b41d515a157e49d54451edf50 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 fd145259361ddd0773500307ce8820fa7873db8b..e3318edb233dbe91b8e35f9fe1a7fff58508fb6b 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 9a5721b8ff96f83edee065f8d27d933e3aad33ac..48f7b3bf4e8c3d2eec3bb413d33d99b6760ce3be 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 6c7a5104a4cd108b526ec8754c2dc0bd7fa99102..7087c736daa520fd22306e6cc39000b7c9aa08c0 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 41052e512d92f5731a6f02e6fd3fe1f2eaa8e925..0d1e6ee3e99246d5b613641237df8ea01a1dcfd2 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 1794c753954ab95cd56cdbdd85023bc69ba3d39c..4eb93b2b6473bcce92f912908a0790485db15e85 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 ef6d836a4d09745d57ba42e2496dd725f85c57ea..83c965c65386da1ce9b8772d5fe3b7193f852f13 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 93bdc475eb00696c96216d0d1679334bb02ec832..16454a922e2ba9ff2bc532cb0b896997b991a02e 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 c970b326ef23d65b34731c174380c77d960fd48c..0c69a46a62ec7f9b082679e7a24f24fb0ffe321e 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 f788e8b5e8552211bc9655f2de8a43d842dc8a3a..10339551030767cd64428686bcb66e0a186c530d 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 4faeb47fa5e11377408c71c407a4f8c5afffa294..39761837608d3a2ff059356e1049779580182952 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 b77693e2c209c9a85289228158f0f6d13d70dae2..213c0fc7fdc9058913bc3b0da38a7d7ef20455e2 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 b0cac0c342e1e83a9da5dc7b5f01edd48cb12136..0819e688a3986586200a3131380445a084ff5291 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 0f6d69dabca1eca22a345be1dd42768517bd60a1..1b41e6776152a446b5fda48a1db3fd4594083b99 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 dd7a84ee78e1129db36e7ab625731666b05125ef..54c59d0b6608f88bcf3f58beda8e6cc92d646b99 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 e51b09a04d52ec3a50cd34c95f20ffa82a37f716..24884cac19ced8f9e6c0cae03787f7d8cb771880 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 47244102281e8432775d55fc55bee0d1bd9ee2e7..fdcee326bfbc0c2b579fc48b758a224dfa74a205 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 e539c3e7f4ad1a64d3f308108e05ebd9a794f1b4..88e2e835f14aeec7247005514fcd6cc9ffc4d3fa 100644 (file)
@@ -7,29 +7,40 @@
  * 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_OFFLINE              0xe
 #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 82e81c542e4350271c68bc1bbddded380d8811d1..272755d03e5aca23785e859c896d9f1ca71635c7 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;
@@ -2130,6 +2130,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
  */
@@ -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 d90dbb0f1a69753a6654413587d9da31a32bdbaa..207b340498a3645231dbb2ae449e48f052e3313f 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 3503996d7d10f667c4328c20ce8101e82b2b5c8e..dae2ad6a669e277d9c95c67098c20cc233dbede6 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 b662f89dedac39b0fe824e4d31583a9dc4553986..0e34cda3271e9bb3291b06a934c1ef7136488811 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 b9a3394fe479225fe2bd16c84cb19bd448be99a3..c87959f12760462ca76740737e7cc839bdd4fc58 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 579128abe3f57e0f49a1689df47bffb4e4e307c8..47b63b094cdcacd0ace2e6fe2dee7c18d4091b49 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 d1ae4c5c3ffd05c5a849183387ac949cb1fed42b..2f5d77932c80052448ab752fdc32d6942b3fc50c 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 131327ac7f5b947652539dda2eef052ba464854b..4ffe5f2ec0e98c956944d2f72e692e5f353c73b5 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 d9b92b2c524d4f055a035f4343175fbdd3877c4c..52ae54e60105652df99df8e64a619a5ba9958ab6 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 074539558a542d5a38dc4f911f0032dc4ed0c998..021c3f4a4f004a8e308825612d0923c48ebe755f 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 9c642e02cba15e37c149800a2ff5fa2a1c42c920..03538994d2f7e1ee817d5b594fd33174079abaf3 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 250009909d497119fb356fd678c674f094a37185..70c638f730af078e25e4ca75b45372e3d16ac24a 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 b9a6ec0aa5fe8fc0a76f4833fe0591ebdea7e155..f697f8baec5418d13484904bf3730880f90639fd 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 81e945eefbbdd0572181d84e3cd9d014895e4eb7..91953da0f62329af488a91eaa1d83be6fda68f08 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 0204952fe4d3d9c58a5e0818dc576b9c2736c0cd..be912b36daae63cc4ca46ab51a75383be8b8cb39 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 474cd44fac14d61b530a6100d5e6060086777560..6b88a9958f6126ab9267cc1c23e97dd8e59b5a57 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 0dd54a44abcf4da28a4dd560de57043f7bc91ff8..752863acecb8ce5716d99fc81172f52d8568937a 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 0e5a1caed176878d82063a639d81b58e1bceecd2..479ec5621a4eafd9eb10e9652577a608dc8b3e4d 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 4e0050840a72833ddcc42c108daaf28ed1fc5b19..c6932fb53a8dd32f08f235fc98ac0a72ff6d5104 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 4859505ae2ed3da7d0a2aec8a15878529c6a71bb..ae52c08dad09071114e730c44b142f49bde1924e 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 03a567199bbe313cd36dd0a4d62e3d9f7cab957d..f1d511a9475b4dc29ec28864cb3b948e8ba13183 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 2b86f8e0fb58f965f637c82e3206f3e4d83c6e51..71630a2af42ccf5f2eb48ea79793cadca86ab5c6 100644 (file)
@@ -1855,6 +1855,9 @@ static struct console sercons = {
  */
 static int __init amiserial_console_init(void)
 {
+       if (!MACH_IS_AMIGA)
+               return -ENODEV;
+
        register_console(&sercons);
        return 0;
 }
index c193af6a628f45758a7fcd59b1b0823e57327655..636c9baad7a58b76fc740bd49794303cfcc472ef 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 0e888621f48464256b0375614d950f1dab43a8e4..7332e2ca46151b112a81775f1b4680ad51ce3ea3 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 7cdd1eb9406c11ccb4870560490f0c6036f92032..0f74945af624962266803ce242ef10509906a6a4 100644 (file)
@@ -768,7 +768,7 @@ static size_t __process_echoes(struct tty_struct *tty)
         * data at the tail to prevent a subsequent overrun */
        while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
                if (echo_buf(ldata, tail) == ECHO_OP_START) {
-                       if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB)
+                       if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
                                tail += 3;
                        else
                                tail += 2;
@@ -1998,7 +1998,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
                found = 1;
 
        size = N_TTY_BUF_SIZE - tail;
-       n = (found + eol + size) & (N_TTY_BUF_SIZE - 1);
+       n = eol - tail;
+       if (n > 4096)
+               n += 4096;
+       n += found;
        c = n;
 
        if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
@@ -2243,18 +2246,19 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
                if (time)
                        timeout = time;
        }
-       mutex_unlock(&ldata->atomic_read_lock);
-       remove_wait_queue(&tty->read_wait, &wait);
+       n_tty_set_room(tty);
+       up_read(&tty->termios_rwsem);
 
+       remove_wait_queue(&tty->read_wait, &wait);
        if (!waitqueue_active(&tty->read_wait))
                ldata->minimum_to_wake = minimum;
 
+       mutex_unlock(&ldata->atomic_read_lock);
+
        __set_current_state(TASK_RUNNING);
        if (b - buf)
                retval = b - buf;
 
-       n_tty_set_room(tty);
-       up_read(&tty->termios_rwsem);
        return retval;
 }
 
index f3b306efaa591d518b1b894253e0714ea3cba5c6..23329918f2292b088eac724156d743784460866c 100644 (file)
@@ -41,7 +41,7 @@ config SERIAL_8250_DEPRECATED_OPTIONS
          accept kernel parameters in both forms like 8250_core.nr_uarts=4 and
          8250.nr_uarts=4. We now renamed the module back to 8250, but if
          anybody noticed in 3.7 and changed their userspace we still have to
-         keep the 8350_core.* options around until they revert the changes
+         keep the 8250_core.* options around until they revert the changes
          they already did.
 
          If 8250 is built as a module, this adds 8250_core alias instead. 
index 481b781b26e370e23fea8d2088311562c15cde87..e9d420ff39310741212eb77e194c13fca2662b18 100644 (file)
@@ -2052,6 +2052,9 @@ static int __init pmz_console_init(void)
        /* Probe ports */
        pmz_probe();
 
+       if (pmz_ports_count == 0)
+               return -ENODEV;
+
        /* TODO: Autoprobe console based on OF */
        /* pmz_console.index = i; */
        register_console(&pmz_console);
index 537750261aaa2fe5a5e2de8fdb3e6ee0adf8fe4d..7d8103cd3e2ec56eacbb5a5d3f3f332597e190ff 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 3a1a01af9a805b38b05f1833eefa80400072f4f4..c74a00ad7add80254ddf98dbaf88ed0725a540e0 100644 (file)
@@ -2086,6 +2086,7 @@ retry_open:
                        filp->f_op = &tty_fops;
                goto retry_open;
        }
+       clear_bit(TTY_HUPPED, &tty->flags);
        tty_unlock(tty);
 
 
index aa491627a45ba83b51a881e0f5b7226a26139933..892cc96466ebbf2d17d744eab9dd2684d7fca902 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 db535b0aa172c85b3787a476b8fe3adeefbe3e6f..fed7f68d025d1174e9e6882c3cba59a94de8220d 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 06cec635e703adc3f9859ae8ca53dbb124820cd4..a7c04e24ca484deb233db5dcfd995b73427c4cc0 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 255c14464bf2ea7a30dffab6fa1770c7b030c0aa..4e243c37f17f50ab197582a3ee5d4662e933d368 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 44cf775a86271ccd95cdf9577c283fc899e70d6c..774e8b89cdb593bf951b5c82e97c169f921a6130 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 eccea1df702df3afc45c692de31b23febfb52c37..6c3d7950d2a9e56d5231938493127c0964f04221 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 84657e07dc5d6b914bd88da4697ab2f8c8b22de1..439c951f261b2d492cb78696fe3ad603154406eb 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 e663921eebb6f899db67e352077689b6e84d270f..f175629513ed3dc0f5ad30eb3d8510506a894e1b 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 14317b70b4139081c24093638364b29efc86ca4a..4f2e1b35eb385046af6343290aca9870258922c0 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 36db5d98dd2f5e900d9dafe3bd6e0e10b30e082e..fb80d68f4d3366e5672498da5bd05f3140e7a753 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 00b3a52c1d68766d42cab3c65a1a9405d4a04617..cee9602f9a7b4ed0ca8071e1723137da7bf12d7e 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 7eed957b6014eb09dc2262c47d28b813d21b21ed..85edabfdef5acb6e561ced3a0411bc0e44ad8bf9 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 798ef200b055590395ea1e05969b6a7b77c3aa1a..d5c936cb217fe78bc0f072a602f22c14b61e388f 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 1f572c00a1bec2f13e68861e6dda971e6fef5d40..c444654fc33fb6f7e858824eb8ed816186bf3bda 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 1ba0d683101525b1615cc03beea3524f42318694..c600ccfd6922b8226727c516ba8a980502e05458 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 98917fc872a429a1ab3aee94cd7d499bc1dafa25..a37c69941d30920649f6f25726ce72b038b752fd 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 6b4a4db4404d8f0e2fff5c2225fd40ccb00bc9ec..28b5338fff715141572b9f40f266b62f74f6134a 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 264ad1c583abcc78ad04a8190edfffcfc4aacde5..e36b18b2817b9e655f524d79e8edf753cabece68 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 6df632e0bb555d1841bae04c4a8ee5862a50896d..5be6e919f7850d7d101542b55120163cec935162 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 8c7b8bcbbdc5b83d6f22584a144de9a3dbc3837e..91bd95a64baf7bfe083e6f67ff803f2edc0ce2f9 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 24a517777fa0a83533daa9c8b585278776ca1518..5cf1621def9c690a2e5947e1e712c1d846769379 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 cc6702fc52685f54f28588108b49d293cbac04da..a8961addc59cf110df2a0ff3d5cbdff67b4fbd4a 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 41b84936a521f62b81bec020646a7014e23e51bc..fbb7b94cabfd5816e7532fe0093b579b831b63a8 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 5eee55012e3375915b769e2bbcf05da9adbeff60..12f0b762b528948b15e30fab605860a34c01ba57 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 b3709f9cf5beab1d5e6d8f51136ae90e3bf23726..3a996576343a3b8abb20b8cab7bf65c7b503a803 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 b178e717ef09e025b6984b00b68184e05934f8e8..afe7d17e677634f1fd447a147deeb133ac366f36 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 1c75260b987c13dcd58ef1137c08ac5386709b0a..dee6cc21d27021d3280b0cd7da999e562147aa40 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 37cb09b27b6328e85581955400cb94bf18b66f0e..9fa1f69dac1328d73c45fccd9034032fcb4412d8 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 61566fc47f8441b9249054415acee30301e68b78..a6a2cebb25879242b8408b1537838a67d932b2b3 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 a14a58d9d1107bdbc31a0ff28f0fcad8fdc71906..4eb188b87f8eb881784488e78d3447400499388e 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 5d36d6fb496919d6007a51114d8eee98759d445d..a3b6a5b30f9fe53e885e205ea6f870352f2aa09f 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 f270bb7bc4564126638200c270882da039219c88..f7ae49edb5186d13080fe369aa5cc50f38ef791f 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 bead7740c86a44da31d3b8dc876b20b51a62cd78..dd625cca1ae5669d4e513a8611736c1b82d33a62 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 e621098bf6630e17ff6eb7a578028bc3f3f4a476..a46f5c7ee7ff4e6c11eceb36a59ca7438949e5b5 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 e0574844c3132c01f86b2c66ed72fc67e69a85d3..833e813118489216a8a27901f749f0765bd03e8f 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 cd31b8a2a729f1a720a61a15a4a7ae3c2ad366ba..23ee53240c4c1858f7d8aa0f7530d43dcc44f650 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 3755833430dc3d8146a3f515e68b0a897a400c60..25beb30878d72644e407cf83a4211b656aca9c42 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 fcd599d4e22542a085fffa4619c9f163d4adb114..4a6ae84b42bcc5b2e829a8bec9a51b017d2a63d5 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 19f3c3fc65f4c9af0ffa4a25cf8410e308a5fda8..45b979d9dd13acf223ac4ede12262c51fe6e1e9d 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 2b2ea13d03ea714a28b2d48e2794e9a8d2361636..a72fe9361ddf028ef717959183cc5bdfc3ea0478 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 6130321da3871dc80b020acc232fca4fc0b8cffc..04f8af65acfd568b486c68460ff5227a841cf9a2 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 eb6b5cc98ec68cc48bbfc906f159a368c98ba739..7ae36690c44916305508408d61fdb117af3d7945 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 bc3fb8fe89abd94312dc0a07e8e19f5e66475c6d..db0a34460e57a796a3891cb407d52d2985d20102 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 e24ef6a6e06461aa01731a3e4910d3cc23924b5e..70a240297c6d22f2a7d170a731f70320512f478a 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 693ac3f4de5aaba92d2a2a7f7004be468b1b0b77..b4786bccc42c3c062153ef06ce2419900d84a76d 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 6d90f7a2ce2218e36e72282749dd5ab6c1dcbabe..1b5c25a47b876194561490aba581d84ac5cd27cc 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 8ced2561395640cdeb16fcc679442011a230c97f..e13e65e996aa7b996f18232243642f7298f8532b 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 d964faf1a2505291b15a5e946e4642ef1d292b08..b16013ffacc27c2e091233b4c8dfb353465a8586 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 f4cce6d66a5573b42afb4517a26752fe15b2a5be..41b3979a9d87068c3cbf3ad5ce9cb430d122ae5a 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 d3dcc6988b5f107dd65395aa8459a8a04af4d012..e2bba68ae71e0108707a24dfa9d14feb6c3c974d 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 5580b4fff7fe0dc7fd85376e3313678356cc650f..f20cc53ff71976cdfb48bb956fb83e8fd88b1e91 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 d1afdf684c18c55a5899238dc0843fee40a08cee..2de486a7eea18a058808cbb33a0ec0fa5f3a650b 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 5c3d4df63e6835f534cb57a3eca724ce08c3ba47..a1a3638c579c857c382ec9c253e5f69424e1dbbb 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 dce9ecffd44a95b4fb627003f3f18e7fcf26ac49..40ca5594a336fa36ed38e0dc953c429ce144c547 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 088fd0c9d88811e81dfab7217d612ec950654d69..3b3148c764a3853c9076d9e14a804f92fdea8347 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 173494a681e6faab613bcfda9a8c72a9fafda325..da6fa2b68074b2523d8ba4a9a819d927c4191b8c 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 bf84f788e59261f985ce85dce2a6fade949807e9..9826b59ef73471c1e450e564176c2e49c4fcb283 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 cc9d328086eda5bc343b9d72b8cfd4b4304381ef..6d4f3998e1f6c08e7158fdf1068b3a94c3bad448 100644 (file)
@@ -258,4 +258,3 @@ MODULE_PARM_DESC(nodelay,
                 "(max6373/74 only, default=0)");
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 97d62ee503418822a1e649fa1b8e7059117aaf13..be86ea359eee1c1ffefd1cbae6f6e12f0059f273 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 d0ebebae607c847766b3e9cc7204dad7afad7cac..d82152077fd9fb1c512145c92a8700cb0ccaf385 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 b4341110ad4f43eef5038a9e3d82a7b2644b2ab8..edb31ffd79270c7e8edf86d255215efa2aeaaf28 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 e4cf980192658fa1d02959ba55e69ec03dc24ea0..f9fa5840939617a45a576ceb5edc1bb271780325 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 b15b6efd91a146f08d8bac2cc6664172c79b194c..a0d893b0930e3309969963a8c5422d31e57d3032 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 59cf19eeea07668e76501fa1734e933947f8d79f..231e5b9d5c8e079ec424b9331aca90912f42efae 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 4dd281f2c33f29b275eaa64cfa80a62d0c62c109..fb57103c8ebcc2e6dcee213d12527cdc81a83aed 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 af88ffd1068f57ebd50354c7a6dd06fdf54af1f4..09cf0135e8acd3078d2480870109984ece309301 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 4ea5fcccac02d1ee6f933cc1e5b8b8e0260da3a4..44edca66d564195a8e20b54f20c8b4cc389eca2c 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 5afb89b48650a4326298933cf01ec69f3fe84fb7..5211d56b368183ca1b1dc7de2bfe97adcde90efd 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 33e49a7f889fcb33c553b126a9495383a414164a..e936f15dc7c7d3b2590478170a648cb2194b8d43 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 7890f84edf760743094ac32c27b712c08fc823a4..b4864f254b48940b7fd9b7febb368b6608af4716 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 7b14d184792705d3b60ea6fba20041be351a186d..b731b5d129bedee728ca195bc032bb24918f5ef0 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 329bc60ad7a23537b42f6871128f62e37e4e8916..0cdfee266690b70fccc93eee4573f0b0cb65766a 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 b30bd430f59120b812dac8feb1732d2edb2f3f22..1bdcc313e1d9f713dc24c8b4cf7e2f7e9c312574 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 1b62a7dfcc95c743b97c085202ef233b46c34f82..882fdcb46ad1f5d9610b545eacc665aa01b460cc 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 9cf6bc7a234f8d7bad6df0cd2733fead4cf11301..71e78ef4b736a1b035e69f5cef063b3a528aaebc 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 b0f116c2fd533b9d3a63361696d793e3a38d6a23..082d062629592970f741967fe4a458ce7bd1df2f 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 23aad7c6bf5d67dac3c4f43064186dc689fe292a..7d8fd041ee250d3759fc2b86519012a09ded76e2 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 ccd6b29e21bfef2aad0ea86ff58c22ac1acc76c1..e1d39a1e9628dd4ce177b197032bb716083b7506 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 ea5d84a1fdad71ad517eba174d5ef1a0fb8cffb0..3abae50773b88d29281c57dd5aba3451fd4ef861 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 63632ec87c7e7f7064485521a9fa3109b61c7740..2eef58a0cf059837c5055d386bede89e21247c7f 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 719edc8fdeb39c083d797fa72010b79bb50e728b..5f268add17ceffdd9b4aa4466b73804cc5310e31 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 d4781e05f0171bbe577f95d524445362db59624a..da60560ca446e7a1023c1707a0615c15e4f3af3c 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 0c3e9f66ef7713bc88e37c48353fb646197f9ff8..a1c502e0d8ece4e58dc6cbfed36df21c53e81e45 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 90d5527ca8863663cbd951ae2a656423eff3fb1e..a517d8bae7578d707585753e81c44fe2353f19c3 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 3fb83b0c28c23a58862b55f482ed2e8c3c0003c9..3b9fff9dcf654ba6abfb09f970a7d288147d7083 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 707e027e50025f4fbff1c73215ede8a3ee4491df..f353e18b1a82fc65221a0dcf9efa7bf696eedbe3 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 af7b136b1874912b735786471421e64208101057..b96127ea3de16322927da5c1351aecbacbd2843d 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 8ae7c282d465ade3be2c3f08efe91968bf2bb503..836377cf92710a03af1a158cd69edfa94a67794b 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 5bca794577687046f4b978c58066f7e7ade7f516..f9b8e06f355808e763ca7a1f7f2e4511fe33c88f 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 6d665f9c1d586393224594df0d2ae57bd9412b29..445ea1ad1fa9abc7fcef53b63e5d0025032f9f07 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 b68b1e519d53d02d77b8a89e73a1779adda2138c..ef2638fee4a8f7037c1f7c342ec1be1347ee3957 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 0e9d8c479c3551e2bb5a51cb9bfcd055f2bcb3e7..ce63a1bbf395fa94ea5833aa0eb98d84aaa5dc75 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 58df98aec1228d794e6b42030192cd6e54012234..3f786ce0a6f2ae93fbb788f7b5dbfc1a06efeac5 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 c97e98dcde62f9eb47e87c65676324aaaaae5209..d667f6b51d35f93aba02547f82653f3dbb189aa4 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 f6caa77151c74a4fc827d933715c92c57da5e0b7..76332d893e122a3293ec3d2b6e7230ad354b04a2 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 c9b0c627fe7e6d8513dd4d43ba18cb65cfbba132..09d4831aa61f8328b6291f97849c9adf06b62b0a 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 88f23c5cfddba5cf646d6adb4de5c4bccd1b9bb4..0fd0e8ae62a833b462b29bd09f8f5e7666e437bf 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 a614d84121c317c8707447803eaaeb1802be7554..e029b5768f2c1379279f034dc24c892b52fc0ec3 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 92f1326f0cfc30dd24b0d829a02548ac42e0d33b..e24b2108287483803c82709d0feec1dae8b29907 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 cd9f3c1e1af44bc14cdd5b116176b9262a783a96..aaf2995d37f4b595d010efa500e89bb1ae5aef5b 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 274be0bfaf244878438411fb025063352cb51f91..ff58cb74671f55ec8220a7d4cf6bbffca6ed2c25 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 7874ae06232b3648757c1529c8df5aaa10729d16..f0483c75ed324b63cf3d63f33a2367325b1c7869 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 5d2c902825c2ec558f0044b3cc07b4c0a4620894..91bf55a2002497eca6de68258515484ba9f13efa 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 25aba6e00a23ffc49f344709d4149d3991958bfd..db0da7ea4fd8d8c12514f1f8552b1f425c84d9bc 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 05d18b4c661b2c15904982e6385944b1ca5515d1..461336c4519faf3acee6dad759030a6f0778876d 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 3045debd5411e26d818309fc3a24c349af0ade4f..0240c60d14e3c04461f376de24f2ed9f9b358344 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 ee4333c01109335088dbbd1a5cd4124f4e2f922b..e0206b5b7d8955526f9598a6cc012fcdfd090873 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 5eec740538824b4e529c725f8e82606b54364240..7355ddd0b2073be68c78e3ced5932942c690876c 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 65a402344933952176170de888311d72fe0bd157..a8e6f87f60c918fb21cb76aae708085fb1a57d44 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 36a54c0e32dd0bded2a4c64393101ba7cde2151d..ee89ba4dea63c932ecad137b49712191b9a9b799 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 d4e47eda41828ea4591a019bde479173e8a4c49a..e243bd01c774274813e48c7dbfdcb56e53af3197 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 92ad33d0cb71c2c897f6f6ff29e695bc62eb35ee..7a42dffd39e59d51ba8748acf8d21d8ff4dfa29d 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 23eae5cb69c21e16d2f71ce2c29e1b307ba3c05c..c794ea1821402dc21e47af3d8b398c9d4f3209e6 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 b232908a61925724bb61bc0f8a09ecbca6b753e6..55ea73f7c70b52e49ca3b4f2b40f5baa064928e9 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 8b3a69a06c39d3c485721d13e58e436af024d99b..5de2063e16d3c10b598b926c4f38f3e38b2547d7 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 c4d2298893b189f61b75da4ed9aa555a90d8878a..62ccf5424ba857e0fe11bb22187bb7dd5d3dad6c 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 18fff88254ebd9bd9e4c76b1df63ecf40dc4e139..188825122aae8a037bccc8255dbd43211ca6a941 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 99db9e1eb8ba47419bc13c5c94803d35537d609d..2f3528e93cb9be0f576ede425b31637d3a92c018 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 1b2277c311d22e31fc276b09cb6bd1ce3b783e39..a224bc74b6b9d34fa5c45f41408a3ed1138098f4 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 f039b104a98e9093cadfb62b040a604628a76b7a..b03dd23feda8104d70536b514ec996fa612c5b89 100644 (file)
 #include "v9fs_vfs.h"
 #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 a29409c1ffe066b00684db073aa365997371e80f..b41c2c9792ff0dc9a68529219af40967421f797e 100644 (file)
@@ -91,7 +91,7 @@ more 2.4 fixes: [Roman Zippel]
 Version 3.11
 ------------
 
-- Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>]
+- Converted to use 2.3.x page cache [Dave Jones]
 - Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>]
 
 Version 3.10
index 823efcbb6ccd1dc7936f77890183cee0ac93ed94..08159ed13649cacbec1825065e24b2b5b61be267 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 2bdb4e25ee77db6c2a5c135b3c726eae734153b9..33d79a4eb92d6e623aa90e2291af39b2b2689d83 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 398cbd517be218bbfc155f06e1bec5e5fe71717c..aa976eced2d2ea8dfa9c0e97ea84da7438626d62 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 a91a6a355cc5d20528f120b076a9a18cac9b9699..1a44e42d602a1b41f60a04ea9a1a290273f7e7dd 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 e15d2b0d8d3b20f3085c18348e9d711682fedc24..0890c83643e944f69e43a22f4151e381e7503f04 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 08cc08f037a633199bffb3fadfa95750302eb25e..c1e0b0caf9cc975c2822cadf9aaaf0c1454dcf91 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 0552a599b28f366ca2a4549458bfedeb8500fdd0..3775947429b28ea07678c4f58877bfcbb5f8bed6 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 71f074e1870b2e9fe183cf338ad2515314155e6e..ac0b39db27d175af15a718a41ac2ebf11c32150f 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 1c47be1872406715183cd1da184f1fd11dabc3be..b50764bef1410c2750b17d943ae3597899b3ee9e 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 6aad98cb343fba0941b9044dabaa9e64697c7354..1499b27b41863e7dfbbe7da134b1a7fb66dece34 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 61b5bcd57b7e320624c2778d2051db03b79f2282..316136bd6dd7eb3899bcf060c3e0bd749f674310 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 0506f40ede8331f8ab7b20a4d2778e49c886fe91..54ab86127f7af49500f6a0bf2bf4b63cd6074c40 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 cbd9523ad09cae0ffff1688371b370a756aef2f5..8d292fbae659eff6a65bd7a8c255d741d998635e 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 9efb94e95858e8c53cb84f587b74a064e8701dc4..2cfc3dfff64f5708f71ec83af691b425b9f01b01 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 79e594e341c7c7156ba28a116f9d66457b3a5e7d..c031ea3fd70f70d64452a3529d3ef99c830a57bf 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 62176ad89846173e4d4987da257166fb90b971ff..8072cfa8a3b16c075e5c381f481e7cb874d9c531 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 5ce2a7da8b113fef13456687fdf4243fa9f1c2ba..53059df350f8f0bb18edc3e4749b473b434a9709 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 4b86916073737a175770605de4a25b862faa6929..41422a3de8ed03f66c7341d8289c1626097a4ff7 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 d58bef130a41984ac7e3172aad43eb87547af64b..45d98d01028f7cdac43461a7acd42f5e17599f32 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 51731b76900de55e8350d5795feacc61a9050d02..8e457fca0a0ba5c04afb84414ccd640821a640d1 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 6dbc645f1f3d00e9b05e290aea4ce05e8f95617a..19620c58f096ef2dd8f0a5810ff632c575e0f5ee 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 61adc44b78053331ef5bc51045f55c6f275604d4..93fba716d7f82f517ad082a65354611d1afecdd2 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 4f53159bdb9d4fd9d4421cab7399abe562467bc8..6f384886028386f2f069756ef18e757b10ba9dbf 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 72da4df53c9a224d7a5106a907fc4ba4e08f5ebb..82d0342763c54d652982d2818ab3b9b7f8ffc1ec 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 b4f9904c4c6b2ed5f0da30ad664baf33100a879c..057be95b1e1e5894bdbe3525d0f5f8f2b3860650 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 e737f92cf6d0b69ffbcc1e94882abc8693c3e3a5..0cf4977ef70dffb69552c96f108b9b643dfbc152 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 e0b7034d6343eb053d195834cdb88beef7c57689..ec82fae070975fcdb6d60879ba34cc001884afbf 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 2c66ddbbe670e0ab80753dc41eb4054aacd00388..ab485e57b6fe6c7ea14f580976dd46c7efd88ffb 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 51e3afa7835479e00fa346241cfccd10b6e60a80..f1a77449d032b1fbd481eb16cd232653e5bfbefa 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 9d46f60cb9439ab3a41ab83f2f323f13aa564590..a111622598b0b23f82c84352e6b782361e079526 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 c702cb62f78a310c8d430fe0d98cfb1173479a0f..69582d5b69d1f6064a77a409760a3ba1886b6d92 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 0c0b35612d7ad1fc5f5c7db5d267e70f319d8529..9b0450f7ac20aa1d5488e2408b80768deb7438ae 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 0088bedc8631f6b5d1ca53924e894e55fa614397..417053b171817742e64e53e579d1daf8c1a9b7cc 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 d0ecfbd9cc9fc5e07a23173b0d1482d5eb9fb75d..24ac21840a9a0797cdb086a58e90e64a3c1481ad 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 4a355726151ec05dd8e1110745648949888781e8..ce459a7cb16dae48e233587cb28d271c4403f297 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 a18e0e23f6a6742cd21277702ed6599df84c32c3..561e2f16ba3e3ff3b0be72b12b4a052b86082d2a 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 e46e0ed7492555646e58659f4cbb4c94ddf4c4d1..6837fe87f3a6bb9f471f54c596e0d98fe67059a3 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;
@@ -2119,77 +2116,6 @@ out:
        return ret;
 }
 
-/*
- * 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
  */
@@ -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 e913328d0f2adc9c3beb2c37556a479c7615d2fa..2d8ac1bf0cf9b9d613215000758cbfa312b4962e 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 58087762577683ad5a3611aab384c0cb7ba9b265..b353bc806ca066be17600283ccc5d1a181af3426 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 8c81bdc1ef9bae82c92e5a8836a0f911c1547a55..c6a872a8a46862948e93c343cdd0c7479caf3883 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 5c2af8491621ced0d1b35110210143a09a1aa418..7657d115067d3f8ae1d5cc39a53410d8a1011673 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 94e05c1f118a7045b4bd40e2b03546347371b461..76928ca97741c61ca4c65513266bbc15b2502d05 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 79f057c0619a5cfe29a6e47dcd3ebeccebce9809..9f7fc51ca334864b72336e127d786047dfb1f5de 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 dd0dea3766f740a0c58d741a4a0b1c63fd6b8038..fbda90004fe9ef08ab0b93b628485c63ee33d1e0 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 043b215769c2c68c538ea147d5cde0721221b833..92303f42baaa92d5d845edddff1f8600fc46518e 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 b72f540c8b295d1be0b3642d695ee0b8b75ebed1..8b3cd142b3734dc0161c479b9bfcc9eb144cdaab 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 6df8bd481425379006912990ee6f9461eaf3cf1b..1e561c059539542e83a118edb003ffabca08506b 100644 (file)
@@ -216,7 +216,7 @@ static int readpage_nounlock(struct file *filp, struct page *page)
        }
        SetPageUptodate(page);
 
-       if (err == 0)
+       if (err >= 0)
                ceph_readpage_to_fscache(inode, page);
 
 out:
index 7db2e6ca4b8f0b07146c137a80e24567a03d3e43..8c44fdd4e1c39f836b2c8a9b2a7a025f1844d3b3 100644 (file)
@@ -324,6 +324,9 @@ void ceph_invalidate_fscache_page(struct inode* inode, struct page *page)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
 
+       if (!PageFsCache(page))
+               return;
+
        fscache_wait_on_page_write(ci->fscache, page);
        fscache_uncache_page(ci->fscache, page);
 }
index 13976c33332ec1fd7ca3999053b15b7079c5ab31..3c0a4bd7499645ca8bf90fd1a6ba16f6831c164c 100644 (file)
@@ -897,7 +897,7 @@ static int __ceph_is_any_caps(struct ceph_inode_info *ci)
  * caller should hold i_ceph_lock.
  * caller will not hold session s_mutex if called from destroy_inode.
  */
-void __ceph_remove_cap(struct ceph_cap *cap)
+void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release)
 {
        struct ceph_mds_session *session = cap->session;
        struct ceph_inode_info *ci = cap->ci;
@@ -909,6 +909,16 @@ void __ceph_remove_cap(struct ceph_cap *cap)
 
        /* remove from session list */
        spin_lock(&session->s_cap_lock);
+       /*
+        * s_cap_reconnect is protected by s_cap_lock. no one changes
+        * s_cap_gen while session is in the reconnect state.
+        */
+       if (queue_release &&
+           (!session->s_cap_reconnect ||
+            cap->cap_gen == session->s_cap_gen))
+               __queue_cap_release(session, ci->i_vino.ino, cap->cap_id,
+                                   cap->mseq, cap->issue_seq);
+
        if (session->s_cap_iterator == cap) {
                /* not yet, we are iterating over this very cap */
                dout("__ceph_remove_cap  delaying %p removal from session %p\n",
@@ -1023,7 +1033,6 @@ void __queue_cap_release(struct ceph_mds_session *session,
        struct ceph_mds_cap_release *head;
        struct ceph_mds_cap_item *item;
 
-       spin_lock(&session->s_cap_lock);
        BUG_ON(!session->s_num_cap_releases);
        msg = list_first_entry(&session->s_cap_releases,
                               struct ceph_msg, list_head);
@@ -1052,7 +1061,6 @@ void __queue_cap_release(struct ceph_mds_session *session,
                     (int)CEPH_CAPS_PER_RELEASE,
                     (int)msg->front.iov_len);
        }
-       spin_unlock(&session->s_cap_lock);
 }
 
 /*
@@ -1067,12 +1075,8 @@ void ceph_queue_caps_release(struct inode *inode)
        p = rb_first(&ci->i_caps);
        while (p) {
                struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node);
-               struct ceph_mds_session *session = cap->session;
-
-               __queue_cap_release(session, ceph_ino(inode), cap->cap_id,
-                                   cap->mseq, cap->issue_seq);
                p = rb_next(p);
-               __ceph_remove_cap(cap);
+               __ceph_remove_cap(cap, true);
        }
 }
 
@@ -2791,7 +2795,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
                        }
                        spin_unlock(&mdsc->cap_dirty_lock);
                }
-               __ceph_remove_cap(cap);
+               __ceph_remove_cap(cap, false);
        }
        /* else, we already released it */
 
@@ -2931,9 +2935,12 @@ void ceph_handle_caps(struct ceph_mds_session *session,
        if (!inode) {
                dout(" i don't have ino %llx\n", vino.ino);
 
-               if (op == CEPH_CAP_OP_IMPORT)
+               if (op == CEPH_CAP_OP_IMPORT) {
+                       spin_lock(&session->s_cap_lock);
                        __queue_cap_release(session, vino.ino, cap_id,
                                            mseq, seq);
+                       spin_unlock(&session->s_cap_lock);
+               }
                goto flush_cap_releases;
        }
 
index 868b61d56cac77f3a8328d5ba4851ec7947fe827..2a0bcaeb189acd18b124aff8d54619667fd97bf2 100644 (file)
@@ -352,8 +352,18 @@ more:
                }
 
                /* note next offset and last dentry name */
+               rinfo = &req->r_reply_info;
+               if (le32_to_cpu(rinfo->dir_dir->frag) != frag) {
+                       frag = le32_to_cpu(rinfo->dir_dir->frag);
+                       if (ceph_frag_is_leftmost(frag))
+                               fi->next_offset = 2;
+                       else
+                               fi->next_offset = 0;
+                       off = fi->next_offset;
+               }
                fi->offset = fi->next_offset;
                fi->last_readdir = req;
+               fi->frag = frag;
 
                if (req->r_reply_info.dir_end) {
                        kfree(fi->last_name);
@@ -363,7 +373,6 @@ more:
                        else
                                fi->next_offset = 0;
                } else {
-                       rinfo = &req->r_reply_info;
                        err = note_last_dentry(fi,
                                       rinfo->dir_dname[rinfo->dir_nr-1],
                                       rinfo->dir_dname_len[rinfo->dir_nr-1]);
index 8549a48115f71b23e1f35ef444caf3eb32dbced3..9a8e396aed89a43a0c824c3b682f96ac817ebc1c 100644 (file)
@@ -577,6 +577,8 @@ static int fill_inode(struct inode *inode,
        int issued = 0, implemented;
        struct timespec mtime, atime, ctime;
        u32 nsplits;
+       struct ceph_inode_frag *frag;
+       struct rb_node *rb_node;
        struct ceph_buffer *xattr_blob = NULL;
        int err = 0;
        int queue_trunc = 0;
@@ -751,15 +753,38 @@ no_change:
        /* FIXME: move me up, if/when version reflects fragtree changes */
        nsplits = le32_to_cpu(info->fragtree.nsplits);
        mutex_lock(&ci->i_fragtree_mutex);
+       rb_node = rb_first(&ci->i_fragtree);
        for (i = 0; i < nsplits; i++) {
                u32 id = le32_to_cpu(info->fragtree.splits[i].frag);
-               struct ceph_inode_frag *frag = __get_or_create_frag(ci, id);
-
-               if (IS_ERR(frag))
-                       continue;
+               frag = NULL;
+               while (rb_node) {
+                       frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+                       if (ceph_frag_compare(frag->frag, id) >= 0) {
+                               if (frag->frag != id)
+                                       frag = NULL;
+                               else
+                                       rb_node = rb_next(rb_node);
+                               break;
+                       }
+                       rb_node = rb_next(rb_node);
+                       rb_erase(&frag->node, &ci->i_fragtree);
+                       kfree(frag);
+                       frag = NULL;
+               }
+               if (!frag) {
+                       frag = __get_or_create_frag(ci, id);
+                       if (IS_ERR(frag))
+                               continue;
+               }
                frag->split_by = le32_to_cpu(info->fragtree.splits[i].by);
                dout(" frag %x split by %d\n", frag->frag, frag->split_by);
        }
+       while (rb_node) {
+               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+               rb_node = rb_next(rb_node);
+               rb_erase(&frag->node, &ci->i_fragtree);
+               kfree(frag);
+       }
        mutex_unlock(&ci->i_fragtree_mutex);
 
        /* were we issued a capability? */
@@ -1250,8 +1275,20 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
        int err = 0, i;
        struct inode *snapdir = NULL;
        struct ceph_mds_request_head *rhead = req->r_request->front.iov_base;
-       u64 frag = le32_to_cpu(rhead->args.readdir.frag);
        struct ceph_dentry_info *di;
+       u64 r_readdir_offset = req->r_readdir_offset;
+       u32 frag = le32_to_cpu(rhead->args.readdir.frag);
+
+       if (rinfo->dir_dir &&
+           le32_to_cpu(rinfo->dir_dir->frag) != frag) {
+               dout("readdir_prepopulate got new frag %x -> %x\n",
+                    frag, le32_to_cpu(rinfo->dir_dir->frag));
+               frag = le32_to_cpu(rinfo->dir_dir->frag);
+               if (ceph_frag_is_leftmost(frag))
+                       r_readdir_offset = 2;
+               else
+                       r_readdir_offset = 0;
+       }
 
        if (req->r_aborted)
                return readdir_prepopulate_inodes_only(req, session);
@@ -1315,7 +1352,7 @@ retry_lookup:
                }
 
                di = dn->d_fsdata;
-               di->offset = ceph_make_fpos(frag, i + req->r_readdir_offset);
+               di->offset = ceph_make_fpos(frag, i + r_readdir_offset);
 
                /* inode */
                if (dn->d_inode) {
index b7bda5d9611da031aaf6f104ece9fa6351993070..d90861f452107cc47b7242e8ea66dc1257f7c235 100644 (file)
@@ -43,6 +43,7 @@
  */
 
 struct ceph_reconnect_state {
+       int nr_caps;
        struct ceph_pagelist *pagelist;
        bool flock;
 };
@@ -443,6 +444,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
        INIT_LIST_HEAD(&s->s_waiting);
        INIT_LIST_HEAD(&s->s_unsafe);
        s->s_num_cap_releases = 0;
+       s->s_cap_reconnect = 0;
        s->s_cap_iterator = NULL;
        INIT_LIST_HEAD(&s->s_cap_releases);
        INIT_LIST_HEAD(&s->s_cap_releases_done);
@@ -642,6 +644,8 @@ static void __unregister_request(struct ceph_mds_client *mdsc,
                req->r_unsafe_dir = NULL;
        }
 
+       complete_all(&req->r_safe_completion);
+
        ceph_mdsc_put_request(req);
 }
 
@@ -986,7 +990,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
        dout("removing cap %p, ci is %p, inode is %p\n",
             cap, ci, &ci->vfs_inode);
        spin_lock(&ci->i_ceph_lock);
-       __ceph_remove_cap(cap);
+       __ceph_remove_cap(cap, false);
        if (!__ceph_is_any_real_caps(ci)) {
                struct ceph_mds_client *mdsc =
                        ceph_sb_to_client(inode->i_sb)->mdsc;
@@ -1231,9 +1235,7 @@ static int trim_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg)
        session->s_trim_caps--;
        if (oissued) {
                /* we aren't the only cap.. just remove us */
-               __queue_cap_release(session, ceph_ino(inode), cap->cap_id,
-                                   cap->mseq, cap->issue_seq);
-               __ceph_remove_cap(cap);
+               __ceph_remove_cap(cap, true);
        } else {
                /* try to drop referring dentries */
                spin_unlock(&ci->i_ceph_lock);
@@ -1416,7 +1418,6 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc,
        unsigned num;
 
        dout("discard_cap_releases mds%d\n", session->s_mds);
-       spin_lock(&session->s_cap_lock);
 
        /* zero out the in-progress message */
        msg = list_first_entry(&session->s_cap_releases,
@@ -1443,8 +1444,6 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc,
                msg->front.iov_len = sizeof(*head);
                list_add(&msg->list_head, &session->s_cap_releases);
        }
-
-       spin_unlock(&session->s_cap_lock);
 }
 
 /*
@@ -1875,8 +1874,11 @@ static int __do_request(struct ceph_mds_client *mdsc,
        int mds = -1;
        int err = -EAGAIN;
 
-       if (req->r_err || req->r_got_result)
+       if (req->r_err || req->r_got_result) {
+               if (req->r_aborted)
+                       __unregister_request(mdsc, req);
                goto out;
+       }
 
        if (req->r_timeout &&
            time_after_eq(jiffies, req->r_started + req->r_timeout)) {
@@ -2186,7 +2188,6 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
        if (head->safe) {
                req->r_got_safe = true;
                __unregister_request(mdsc, req);
-               complete_all(&req->r_safe_completion);
 
                if (req->r_got_unsafe) {
                        /*
@@ -2238,8 +2239,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
        err = ceph_fill_trace(mdsc->fsc->sb, req, req->r_session);
        if (err == 0) {
                if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR ||
-                                   req->r_op == CEPH_MDS_OP_LSSNAP) &&
-                   rinfo->dir_nr)
+                                   req->r_op == CEPH_MDS_OP_LSSNAP))
                        ceph_readdir_prepopulate(req, req->r_session);
                ceph_unreserve_caps(mdsc, &req->r_caps_reservation);
        }
@@ -2490,6 +2490,7 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
        cap->seq = 0;        /* reset cap seq */
        cap->issue_seq = 0;  /* and issue_seq */
        cap->mseq = 0;       /* and migrate_seq */
+       cap->cap_gen = cap->session->s_cap_gen;
 
        if (recon_state->flock) {
                rec.v2.cap_id = cpu_to_le64(cap->cap_id);
@@ -2552,6 +2553,8 @@ encode_again:
        } else {
                err = ceph_pagelist_append(pagelist, &rec, reclen);
        }
+
+       recon_state->nr_caps++;
 out_free:
        kfree(path);
 out_dput:
@@ -2579,6 +2582,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc,
        struct rb_node *p;
        int mds = session->s_mds;
        int err = -ENOMEM;
+       int s_nr_caps;
        struct ceph_pagelist *pagelist;
        struct ceph_reconnect_state recon_state;
 
@@ -2610,20 +2614,38 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc,
        dout("session %p state %s\n", session,
             session_state_name(session->s_state));
 
+       spin_lock(&session->s_gen_ttl_lock);
+       session->s_cap_gen++;
+       spin_unlock(&session->s_gen_ttl_lock);
+
+       spin_lock(&session->s_cap_lock);
+       /*
+        * notify __ceph_remove_cap() that we are composing cap reconnect.
+        * If a cap get released before being added to the cap reconnect,
+        * __ceph_remove_cap() should skip queuing cap release.
+        */
+       session->s_cap_reconnect = 1;
        /* drop old cap expires; we're about to reestablish that state */
        discard_cap_releases(mdsc, session);
+       spin_unlock(&session->s_cap_lock);
 
        /* traverse this session's caps */
-       err = ceph_pagelist_encode_32(pagelist, session->s_nr_caps);
+       s_nr_caps = session->s_nr_caps;
+       err = ceph_pagelist_encode_32(pagelist, s_nr_caps);
        if (err)
                goto fail;
 
+       recon_state.nr_caps = 0;
        recon_state.pagelist = pagelist;
        recon_state.flock = session->s_con.peer_features & CEPH_FEATURE_FLOCK;
        err = iterate_session_caps(session, encode_caps_cb, &recon_state);
        if (err < 0)
                goto fail;
 
+       spin_lock(&session->s_cap_lock);
+       session->s_cap_reconnect = 0;
+       spin_unlock(&session->s_cap_lock);
+
        /*
         * snaprealms.  we provide mds with the ino, seq (version), and
         * parent for all of our realms.  If the mds has any newer info,
@@ -2646,11 +2668,18 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc,
 
        if (recon_state.flock)
                reply->hdr.version = cpu_to_le16(2);
-       if (pagelist->length) {
-               /* set up outbound data if we have any */
-               reply->hdr.data_len = cpu_to_le32(pagelist->length);
-               ceph_msg_data_add_pagelist(reply, pagelist);
+
+       /* raced with cap release? */
+       if (s_nr_caps != recon_state.nr_caps) {
+               struct page *page = list_first_entry(&pagelist->head,
+                                                    struct page, lru);
+               __le32 *addr = kmap_atomic(page);
+               *addr = cpu_to_le32(recon_state.nr_caps);
+               kunmap_atomic(addr);
        }
+
+       reply->hdr.data_len = cpu_to_le32(pagelist->length);
+       ceph_msg_data_add_pagelist(reply, pagelist);
        ceph_con_send(&session->s_con, reply);
 
        mutex_unlock(&session->s_mutex);
index c2a19fbbe5177b619b7a3d7e6132b626df8c8508..4c053d099ae4e60400dbcbdcce21844138ba8a47 100644 (file)
@@ -132,6 +132,7 @@ struct ceph_mds_session {
        struct list_head  s_caps;     /* all caps issued by this session */
        int               s_nr_caps, s_trim_caps;
        int               s_num_cap_releases;
+       int               s_cap_reconnect;
        struct list_head  s_cap_releases; /* waiting cap_release messages */
        struct list_head  s_cap_releases_done; /* ready to send */
        struct ceph_cap  *s_cap_iterator;
index 6014b0a3c405cb12dfb62fdac7887f83a4977b96..ef4ac38bb614a911680668fe52f6e7fa272d94ce 100644 (file)
@@ -741,13 +741,7 @@ extern int ceph_add_cap(struct inode *inode,
                        int fmode, unsigned issued, unsigned wanted,
                        unsigned cap, unsigned seq, u64 realmino, int flags,
                        struct ceph_cap_reservation *caps_reservation);
-extern void __ceph_remove_cap(struct ceph_cap *cap);
-static inline void ceph_remove_cap(struct ceph_cap *cap)
-{
-       spin_lock(&cap->ci->i_ceph_lock);
-       __ceph_remove_cap(cap);
-       spin_unlock(&cap->ci->i_ceph_lock);
-}
+extern void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release);
 extern void ceph_put_cap(struct ceph_mds_client *mdsc,
                         struct ceph_cap *cap);
 
index fc6f4f3a1a9d6c4c2f8b0c7afa1eb20d8029febf..4934347321d379b61aa170d0f93dd4468c383000 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 26b1c1dc93f6ef20c8c3d23fc57db320478cc923..f918a998a08758caac54bf8205cd7da7705c1efc 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,10 @@ 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);
+       int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
 };
 
 struct smb_version_values {
@@ -855,6 +859,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 9e5ee34de98633b0d894ddadfdf86cd5bd963159..33df36ef9d52037e69857f1dc7b1c9591acb8251 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 93b29474714a0cdba1356c85c049e874dbc195dd..124aa0230c1b8738edb8d04ca5d764f0a6cc5b12 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 5384c2a640ca6fc06962a01261ca4f8ce890b246..11ff5f116b20e663bf5e0428a940b437d9b232aa 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 7ddddf2e25046af5fceb6fcbe097e2b14750710c..5a5a87240fe2cfa971a17f7b673d06f9b2326e5f 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 867b7cdc794a221a6eb21257d20bdc3f9e516248..36f9ebb93ceba676c363cdf7b973f3936a9dde8f 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 ba54bf6ab1165d6db4b587a898554dd8dc388d2d..77492301cc2b16c0f82ecea64de7bfb02fe31db5 100644 (file)
  */
 
 #include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
 #include "cifs_debug.h"
 #include "cifsfs.h"
 
+#define CIFS_IOCTL_MAGIC       0xCF
+#define CIFS_IOC_COPYCHUNK_FILE        _IOW(CIFS_IOCTL_MAGIC, 3, int)
+
+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 +215,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 CIFS_IOC_COPYCHUNK_FILE:
+                       rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
+                       break;
                default:
                        cifs_dbg(FYI, "unsupported ioctl\n");
                        break;
index 651a5279607b968a255a528e5411e4f4b39ca438..049884552e76385091513111576bdf11703945aa 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 53a75f3d0179231d395611f61f2897c9a52f33de..5940ecabbe6a4f538f7dc91c334ed3017235e4dd 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 384cffe42850cdbdb4365e0ff6d6e8eb1a1fbb87..5f5ba0dc2ee1b9c7b3d26ede9590a185ac6925e0 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 78ff88c467b99b9a17c706032a9ed655f92c58b0..84c012a6aba01fc9a7fcd82ab3548047da63d55b 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 7c2f45c06fc219357fa15d5b46f8aa8ac5449aba..94bd4fbb13d3d28283ec4d705086d784d0f70eaf 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 c571be8cb76ef418ffcca63634fde5e3d39294f5..757da3e54d3dce601b71b97883f3430556040107 100644 (file)
@@ -493,6 +493,157 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
        SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
 }
 
+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;
+       struct copychunk_ioctl_rsp *retbuf = NULL;
+       struct cifs_tcon *tcon;
+       int chunks_copied = 0;
+       bool chunk_sizes_updated = false;
+
+       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)
+               goto cchunk_out;
+
+       /* For now array only one chunk long, will make more flexible later */
+       pcchunk->ChunkCount = __constant_cpu_to_le32(1);
+       pcchunk->Reserved = 0;
+       pcchunk->Reserved2 = 0;
+
+       tcon = tlink_tcon(trgtfile->tlink);
+
+       while (len > 0) {
+               pcchunk->SourceOffset = cpu_to_le64(src_off);
+               pcchunk->TargetOffset = cpu_to_le64(dest_off);
+               pcchunk->Length =
+                       cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
+
+               /* Request server copy to target from src identified by key */
+               rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
+                       trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
+                       true /* is_fsctl */, (char *)pcchunk,
+                       sizeof(struct copychunk_ioctl), (char **)&retbuf,
+                       &ret_data_len);
+               if (rc == 0) {
+                       if (ret_data_len !=
+                                       sizeof(struct copychunk_ioctl_rsp)) {
+                               cifs_dbg(VFS, "invalid cchunk response size\n");
+                               rc = -EIO;
+                               goto cchunk_out;
+                       }
+                       if (retbuf->TotalBytesWritten == 0) {
+                               cifs_dbg(FYI, "no bytes copied\n");
+                               rc = -EIO;
+                               goto cchunk_out;
+                       }
+                       /*
+                        * Check if server claimed to write more than we asked
+                        */
+                       if (le32_to_cpu(retbuf->TotalBytesWritten) >
+                           le32_to_cpu(pcchunk->Length)) {
+                               cifs_dbg(VFS, "invalid copy chunk response\n");
+                               rc = -EIO;
+                               goto cchunk_out;
+                       }
+                       if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
+                               cifs_dbg(VFS, "invalid num chunks written\n");
+                               rc = -EIO;
+                               goto cchunk_out;
+                       }
+                       chunks_copied++;
+
+                       src_off += le32_to_cpu(retbuf->TotalBytesWritten);
+                       dest_off += le32_to_cpu(retbuf->TotalBytesWritten);
+                       len -= le32_to_cpu(retbuf->TotalBytesWritten);
+
+                       cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n",
+                               le32_to_cpu(retbuf->ChunksWritten),
+                               le32_to_cpu(retbuf->ChunkBytesWritten),
+                               le32_to_cpu(retbuf->TotalBytesWritten));
+               } else if (rc == -EINVAL) {
+                       if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
+                               goto cchunk_out;
+
+                       cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n",
+                               le32_to_cpu(retbuf->ChunksWritten),
+                               le32_to_cpu(retbuf->ChunkBytesWritten),
+                               le32_to_cpu(retbuf->TotalBytesWritten));
+
+                       /*
+                        * Check if this is the first request using these sizes,
+                        * (ie check if copy succeed once with original sizes
+                        * and check if the server gave us different sizes after
+                        * we already updated max sizes on previous request).
+                        * if not then why is the server returning an error now
+                        */
+                       if ((chunks_copied != 0) || chunk_sizes_updated)
+                               goto cchunk_out;
+
+                       /* Check that server is not asking us to grow size */
+                       if (le32_to_cpu(retbuf->ChunkBytesWritten) <
+                                       tcon->max_bytes_chunk)
+                               tcon->max_bytes_chunk =
+                                       le32_to_cpu(retbuf->ChunkBytesWritten);
+                       else
+                               goto cchunk_out; /* server gave us bogus size */
+
+                       /* No need to change MaxChunks since already set to 1 */
+                       chunk_sizes_updated = true;
+               }
+       }
+
+cchunk_out:
+       kfree(pcchunk);
+       return rc;
+}
+
 static int
 smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
                struct cifs_fid *fid)
@@ -1017,6 +1168,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 +1242,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 +1318,8 @@ 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,
+       .validate_negotiate = smb3_validate_negotiate,
 };
 
 struct smb_version_values smb20_values = {
index 8ab05b0d6778f99343d5740ff037ccbfad7bbef8..2013234b73adc47a5a34cb907ce0b818f65a16f5 100644 (file)
@@ -454,6 +454,81 @@ neg_exit:
        return rc;
 }
 
+int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
+{
+       int rc = 0;
+       struct validate_negotiate_info_req vneg_inbuf;
+       struct validate_negotiate_info_rsp *pneg_rsp;
+       u32 rsplen;
+
+       cifs_dbg(FYI, "validate negotiate\n");
+
+       /*
+        * validation ioctl must be signed, so no point sending this if we
+        * can not sign it.  We could eventually change this to selectively
+        * sign just this, the first and only signed request on a connection.
+        * This is good enough for now since a user who wants better security
+        * would also enable signing on the mount. Having validation of
+        * negotiate info for signed connections helps reduce attack vectors
+        */
+       if (tcon->ses->server->sign == false)
+               return 0; /* validation requires signing */
+
+       vneg_inbuf.Capabilities =
+                       cpu_to_le32(tcon->ses->server->vals->req_capabilities);
+       memcpy(vneg_inbuf.Guid, cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
+
+       if (tcon->ses->sign)
+               vneg_inbuf.SecurityMode =
+                       cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
+       else if (global_secflags & CIFSSEC_MAY_SIGN)
+               vneg_inbuf.SecurityMode =
+                       cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
+       else
+               vneg_inbuf.SecurityMode = 0;
+
+       vneg_inbuf.DialectCount = cpu_to_le16(1);
+       vneg_inbuf.Dialects[0] =
+               cpu_to_le16(tcon->ses->server->vals->protocol_id);
+
+       rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
+               FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
+               (char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
+               (char **)&pneg_rsp, &rsplen);
+
+       if (rc != 0) {
+               cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
+               return -EIO;
+       }
+
+       if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
+               cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
+               return -EIO;
+       }
+
+       /* check validate negotiate info response matches what we got earlier */
+       if (pneg_rsp->Dialect !=
+                       cpu_to_le16(tcon->ses->server->vals->protocol_id))
+               goto vneg_out;
+
+       if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode))
+               goto vneg_out;
+
+       /* do not validate server guid because not saved at negprot time yet */
+
+       if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
+             SMB2_LARGE_FILES) != tcon->ses->server->capabilities)
+               goto vneg_out;
+
+       /* validate negotiate successful */
+       cifs_dbg(FYI, "validate negotiate info successful\n");
+       return 0;
+
+vneg_out:
+       cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
+       return -EIO;
+}
+
 int
 SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
                const struct nls_table *nls_cp)
@@ -630,6 +705,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 +794,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 +903,9 @@ 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);
+       if (tcon->ses->server->ops->validate_negotiate)
+               rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
 tcon_exit:
        free_rsp_buf(resp_buftype, rsp);
        kfree(unc_path);
@@ -1204,10 +1291,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
        rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
 
-       if (rc != 0) {
+       if ((rc != 0) && (rc != -EINVAL)) {
                if (tcon)
                        cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
                goto ioctl_exit;
+       } else if (rc == -EINVAL) {
+               if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
+                   (opcode != FSCTL_SRV_COPYCHUNK)) {
+                       if (tcon)
+                               cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+                       goto ioctl_exit;
+               }
        }
 
        /* check if caller wants to look at return data or just return rc */
@@ -2144,11 +2238,9 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
        rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
        rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
 
-       if (rc != 0) {
+       if (rc != 0)
                cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
-               goto out;
-       }
-out:
+
        free_rsp_buf(resp_buftype, rsp);
        kfree(iov);
        return rc;
index 6183b1b7550f068a7f0628e7dab6e701e9ad0d2f..2022c542ea3aa65ddf15cf511ee652d3040b73a2 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,13 +571,25 @@ struct copychunk_ioctl {
        __u32 Reserved2;
 } __packed;
 
-/* Response and Request are the same format */
-struct validate_negotiate_info {
+struct copychunk_ioctl_rsp {
+       __le32 ChunksWritten;
+       __le32 ChunkBytesWritten;
+       __le32 TotalBytesWritten;
+} __packed;
+
+struct validate_negotiate_info_req {
        __le32 Capabilities;
        __u8   Guid[SMB2_CLIENT_GUID_SIZE];
        __le16 SecurityMode;
        __le16 DialectCount;
-       __le16 Dialect[1];
+       __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
+} __packed;
+
+struct validate_negotiate_info_rsp {
+       __le32 Capabilities;
+       __u8   Guid[SMB2_CLIENT_GUID_SIZE];
+       __le16 SecurityMode;
+       __le16 Dialect; /* Dialect in use for the connection */
 } __packed;
 
 #define RSS_CAPABLE    0x00000001
index 313813e4c19b300357bfc5f6db225a3be85d1121..93adc64666f310345b4c6d76bba628741aa4e634 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);
@@ -162,5 +162,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
                      struct smb2_lock_element *buf);
 extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
                            __u8 *lease_key, const __le32 lease_state);
+extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
 
 #endif                 /* _SMB2PROTO_H */
index a4b2391fe66e4e11cea93e7396b987c335d43823..0e538b5c96221f61f55f9a8bff58d6d88cfc836b 100644 (file)
@@ -90,7 +90,7 @@
 #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */
 #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
 #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
-#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */
+#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
 /* Perform server-side data movement */
 #define FSCTL_SRV_COPYCHUNK 0x001440F2
 #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2
index 277bd1be21fd70061fcd8d6694ed65ab4a34abd3..e081acbac2e756372340379fea7a69a84c9c3dd0 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 62406b6959b63389bd503cf6db4ea528266b79a8..bc3fbcd32558fd61823b126997ace2785d7bac21 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 0a38ef8d7f0088579089d101c99a0e12193f22f5..4bdb300b16e2e940bab8eeb07417b7f87914815f 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 60a327863b1122e246b79bf91ecdf23136eccac9..e7cfbaf8d0e2ed66b404c259dcd64c2d4ccd5f54 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 000eae2782b6e905aefa05980ef91c54b81a8274..2f6735dbf1a9ded47999ebc97590221b16eb6925 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 2229a74aeeedaab2a6b6b49f9c4c9309f1369367..b1eaa7a1f82cd0ce03ebb0bad15c2d8f5b6f6e9c 100644 (file)
@@ -313,11 +313,9 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag)
 static long
 ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-       struct file *lower_file = NULL;
+       struct file *lower_file = ecryptfs_file_to_lower(file);
        long rc = -ENOTTY;
 
-       if (ecryptfs_file_to_private(file))
-               lower_file = ecryptfs_file_to_lower(file);
        if (lower_file->f_op->unlocked_ioctl)
                rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
        return rc;
@@ -327,11 +325,9 @@ ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 static long
 ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-       struct file *lower_file = NULL;
+       struct file *lower_file = ecryptfs_file_to_lower(file);
        long rc = -ENOIOCTLCMD;
 
-       if (ecryptfs_file_to_private(file))
-               lower_file = ecryptfs_file_to_lower(file);
        if (lower_file->f_op && lower_file->f_op->compat_ioctl)
                rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
        return rc;
index a8766b880c0783b3adca66bac0e5f0ba76840c70..becc725a195308edfcd518572f1f8784052b1bb6 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 977319fd77f39de88ef66979d1de95f7d846f9a6..7ea097f6b341f06982f3ea3b068de5755b1605e0 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 dc5d572ebd6a41ae5aebbb5f7b73346b3b4c010e..6ea7b1436bbc201e872d6ee18f7321b2e099f156 100644 (file)
@@ -640,6 +640,7 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb)
        struct ext4_group_desc *gdp;
        ext4_group_t i;
        ext4_group_t ngroups = ext4_get_groups_count(sb);
+       struct ext4_group_info *grp;
 #ifdef EXT4FS_DEBUG
        struct ext4_super_block *es;
        ext4_fsblk_t bitmap_count;
@@ -655,7 +656,11 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb)
                gdp = ext4_get_group_desc(sb, i, NULL);
                if (!gdp)
                        continue;
-               desc_count += ext4_free_group_clusters(sb, gdp);
+               grp = NULL;
+               if (EXT4_SB(sb)->s_group_info)
+                       grp = ext4_get_group_info(sb, i);
+               if (!grp || !EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
+                       desc_count += ext4_free_group_clusters(sb, gdp);
                brelse(bitmap_bh);
                bitmap_bh = ext4_read_block_bitmap(sb, i);
                if (bitmap_bh == NULL)
@@ -679,7 +684,11 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb)
                gdp = ext4_get_group_desc(sb, i, NULL);
                if (!gdp)
                        continue;
-               desc_count += ext4_free_group_clusters(sb, gdp);
+               grp = NULL;
+               if (EXT4_SB(sb)->s_group_info)
+                       grp = ext4_get_group_info(sb, i);
+               if (!grp || !EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
+                       desc_count += ext4_free_group_clusters(sb, gdp);
        }
 
        return desc_count;
index d01d62315f7ebb156578ca79c677a5330f042422..e6185031c1ccb5d7ef6c35185d77e5b06d648a5d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/wait.h>
 #include <linux/blockgroup_lock.h>
 #include <linux/percpu_counter.h>
+#include <linux/ratelimit.h>
 #include <crypto/hash.h>
 #ifdef __KERNEL__
 #include <linux/compat.h>
@@ -1314,6 +1315,11 @@ struct ext4_sb_info {
        unsigned long s_es_last_sorted;
        struct percpu_counter s_extent_cache_cnt;
        spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp;
+
+       /* Ratelimit ext4 messages. */
+       struct ratelimit_state s_err_ratelimit_state;
+       struct ratelimit_state s_warning_ratelimit_state;
+       struct ratelimit_state s_msg_ratelimit_state;
 };
 
 static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -1396,7 +1402,18 @@ static inline void ext4_clear_inode_##name(struct inode *inode, int bit) \
        clear_bit(bit + (offset), &EXT4_I(inode)->i_##field);           \
 }
 
+/* Add these declarations here only so that these functions can be
+ * found by name.  Otherwise, they are very hard to locate. */
+static inline int ext4_test_inode_flag(struct inode *inode, int bit);
+static inline void ext4_set_inode_flag(struct inode *inode, int bit);
+static inline void ext4_clear_inode_flag(struct inode *inode, int bit);
 EXT4_INODE_BIT_FNS(flag, flags, 0)
+
+/* Add these declarations here only so that these functions can be
+ * found by name.  Otherwise, they are very hard to locate. */
+static inline int ext4_test_inode_state(struct inode *inode, int bit);
+static inline void ext4_set_inode_state(struct inode *inode, int bit);
+static inline void ext4_clear_inode_state(struct inode *inode, int bit);
 #if (BITS_PER_LONG < 64)
 EXT4_INODE_BIT_FNS(state, state_flags, 0)
 
index 54d52afcdb198d6b55d28a479cfa9e353ab96100..35f65cf4f318d72bce4e79995ddbc85d0e4bbfa3 100644 (file)
@@ -1666,7 +1666,7 @@ int
 ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
                                struct ext4_extent *ex2)
 {
-       unsigned short ext1_ee_len, ext2_ee_len, max_len;
+       unsigned short ext1_ee_len, ext2_ee_len;
 
        /*
         * Make sure that both extents are initialized. We don't merge
@@ -1677,11 +1677,6 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
        if (ext4_ext_is_uninitialized(ex1) || ext4_ext_is_uninitialized(ex2))
                return 0;
 
-       if (ext4_ext_is_uninitialized(ex1))
-               max_len = EXT_UNINIT_MAX_LEN;
-       else
-               max_len = EXT_INIT_MAX_LEN;
-
        ext1_ee_len = ext4_ext_get_actual_len(ex1);
        ext2_ee_len = ext4_ext_get_actual_len(ex2);
 
@@ -1694,7 +1689,7 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
         * as an RO_COMPAT feature, refuse to merge to extents if
         * this can result in the top bit of ee_len being set.
         */
-       if (ext1_ee_len + ext2_ee_len > max_len)
+       if (ext1_ee_len + ext2_ee_len > EXT_INIT_MAX_LEN)
                return 0;
 #ifdef AGGRESSIVE_TEST
        if (ext1_ee_len >= 4)
@@ -1720,7 +1715,6 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
        struct ext4_extent_header *eh;
        unsigned int depth, len;
        int merge_done = 0;
-       int uninitialized = 0;
 
        depth = ext_depth(inode);
        BUG_ON(path[depth].p_hdr == NULL);
@@ -1730,12 +1724,8 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
                if (!ext4_can_extents_be_merged(inode, ex, ex + 1))
                        break;
                /* merge with next extent! */
-               if (ext4_ext_is_uninitialized(ex))
-                       uninitialized = 1;
                ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
                                + ext4_ext_get_actual_len(ex + 1));
-               if (uninitialized)
-                       ext4_ext_mark_uninitialized(ex);
 
                if (ex + 1 < EXT_LAST_EXTENT(eh)) {
                        len = (EXT_LAST_EXTENT(eh) - ex - 1)
@@ -1890,7 +1880,6 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
        struct ext4_ext_path *npath = NULL;
        int depth, len, err;
        ext4_lblk_t next;
-       unsigned uninitialized = 0;
        int mb_flags = 0;
 
        if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
@@ -1942,18 +1931,8 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
                        if (err)
                                return err;
 
-                       /*
-                        * ext4_can_extents_be_merged should have checked
-                        * that either both extents are uninitialized, or
-                        * both aren't. Thus we need to check only one of
-                        * them here.
-                        */
-                       if (ext4_ext_is_uninitialized(ex))
-                               uninitialized = 1;
                        ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
                                        + ext4_ext_get_actual_len(newext));
-                       if (uninitialized)
-                               ext4_ext_mark_uninitialized(ex);
                        eh = path[depth].p_hdr;
                        nearex = ex;
                        goto merge;
@@ -1976,20 +1955,10 @@ prepend:
                        if (err)
                                return err;
 
-                       /*
-                        * ext4_can_extents_be_merged should have checked
-                        * that either both extents are uninitialized, or
-                        * both aren't. Thus we need to check only one of
-                        * them here.
-                        */
-                       if (ext4_ext_is_uninitialized(ex))
-                               uninitialized = 1;
                        ex->ee_block = newext->ee_block;
                        ext4_ext_store_pblock(ex, ext4_ext_pblock(newext));
                        ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex)
                                        + ext4_ext_get_actual_len(newext));
-                       if (uninitialized)
-                               ext4_ext_mark_uninitialized(ex);
                        eh = path[depth].p_hdr;
                        nearex = ex;
                        goto merge;
index 137193ff389b88510f414499116c8a9ee858abe5..0ee59a6644e211b752480364c95e5616ab9e92fd 100644 (file)
@@ -432,7 +432,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent,
                        ext4fs_dirhash(qstr->name, qstr->len, &hinfo);
                        grp = hinfo.hash;
                } else
-                       get_random_bytes(&grp, sizeof(grp));
+                       grp = prandom_u32();
                parent_group = (unsigned)grp % ngroups;
                for (i = 0; i < ngroups; i++) {
                        g = (parent_group + i) % ngroups;
index d9ecbf1113a75798f4d2a5903fd2fc522ce00dba..bae987549dc367736eaf0beeedb29e8e514e46e6 100644 (file)
@@ -994,11 +994,9 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
        struct inode    *dir = dentry->d_parent->d_inode;
        const char      *name = dentry->d_name.name;
        int             namelen = dentry->d_name.len;
-       unsigned short  reclen;
        int             err;
        struct ext4_dir_entry_2 *de;
 
-       reclen = EXT4_DIR_REC_LEN(namelen);
        err = ext4_find_dest_de(dir, inode, iloc->bh,
                                inline_start, inline_size,
                                name, namelen, &de);
@@ -1442,6 +1440,7 @@ int ext4_read_inline_dir(struct file *file,
        if (ret < 0)
                goto out;
 
+       ret = 0;
        sb = inode->i_sb;
        parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode);
        offset = ctx->pos;
index e274e9c1171f9095829aff07224dfff650a85ed2..0757634741187a568cc2255187bcd2cf250d852b 100644 (file)
@@ -2178,6 +2178,9 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd)
  *
  * @handle - handle for journal operations
  * @mpd - extent to map
+ * @give_up_on_write - we set this to true iff there is a fatal error and there
+ *                     is no hope of writing the data. The caller should discard
+ *                     dirty pages to avoid infinite loops.
  *
  * The function maps extent starting at mpd->lblk of length mpd->len. If it is
  * delayed, blocks are allocated, if it is unwritten, we may need to convert
@@ -2295,6 +2298,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
        struct address_space *mapping = mpd->inode->i_mapping;
        struct pagevec pvec;
        unsigned int nr_pages;
+       long left = mpd->wbc->nr_to_write;
        pgoff_t index = mpd->first_page;
        pgoff_t end = mpd->last_page;
        int tag;
@@ -2330,6 +2334,17 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
                        if (page->index > end)
                                goto out;
 
+                       /*
+                        * Accumulated enough dirty pages? This doesn't apply
+                        * to WB_SYNC_ALL mode. For integrity sync we have to
+                        * keep going because someone may be concurrently
+                        * dirtying pages, and we might have synced a lot of
+                        * newly appeared dirty pages, but have not synced all
+                        * of the old dirty pages.
+                        */
+                       if (mpd->wbc->sync_mode == WB_SYNC_NONE && left <= 0)
+                               goto out;
+
                        /* If we can't merge this page, we are done. */
                        if (mpd->map.m_len > 0 && mpd->next_page != page->index)
                                goto out;
@@ -2364,19 +2379,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
                        if (err <= 0)
                                goto out;
                        err = 0;
-
-                       /*
-                        * Accumulated enough dirty pages? This doesn't apply
-                        * to WB_SYNC_ALL mode. For integrity sync we have to
-                        * keep going because someone may be concurrently
-                        * dirtying pages, and we might have synced a lot of
-                        * newly appeared dirty pages, but have not synced all
-                        * of the old dirty pages.
-                        */
-                       if (mpd->wbc->sync_mode == WB_SYNC_NONE &&
-                           mpd->next_page - mpd->first_page >=
-                                                       mpd->wbc->nr_to_write)
-                               goto out;
+                       left--;
                }
                pagevec_release(&pvec);
                cond_resched();
@@ -2420,16 +2423,15 @@ static int ext4_writepages(struct address_space *mapping,
         * because that could violate lock ordering on umount
         */
        if (!mapping->nrpages || !mapping_tagged(mapping, PAGECACHE_TAG_DIRTY))
-               return 0;
+               goto out_writepages;
 
        if (ext4_should_journal_data(inode)) {
                struct blk_plug plug;
-               int ret;
 
                blk_start_plug(&plug);
                ret = write_cache_pages(mapping, wbc, __writepage, mapping);
                blk_finish_plug(&plug);
-               return ret;
+               goto out_writepages;
        }
 
        /*
@@ -2442,8 +2444,10 @@ static int ext4_writepages(struct address_space *mapping,
         * *never* be called, so if that ever happens, we would want
         * the stack trace.
         */
-       if (unlikely(sbi->s_mount_flags & EXT4_MF_FS_ABORTED))
-               return -EROFS;
+       if (unlikely(sbi->s_mount_flags & EXT4_MF_FS_ABORTED)) {
+               ret = -EROFS;
+               goto out_writepages;
+       }
 
        if (ext4_should_dioread_nolock(inode)) {
                /*
@@ -4689,6 +4693,15 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
        inode = dentry->d_inode;
        generic_fillattr(inode, stat);
 
+       /*
+        * If there is inline data in the inode, the inode will normally not
+        * have data blocks allocated (it may have an external xattr block).
+        * Report at least one sector for such files, so tools like tar, rsync,
+        * others doen't incorrectly think the file is completely sparse.
+        */
+       if (unlikely(ext4_has_inline_data(inode)))
+               stat->blocks += (stat->size + 511) >> 9;
+
        /*
         * We can't update i_blocks if the block allocation is delayed
         * otherwise in the case of system crash before the real block
@@ -4700,9 +4713,8 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
         * blocks for this file.
         */
        delalloc_blocks = EXT4_C2B(EXT4_SB(inode->i_sb),
-                               EXT4_I(inode)->i_reserved_data_blocks);
-
-       stat->blocks += delalloc_blocks << (inode->i_sb->s_blocksize_bits-9);
+                                  EXT4_I(inode)->i_reserved_data_blocks);
+       stat->blocks += delalloc_blocks << (inode->i_sb->s_blocksize_bits - 9);
        return 0;
 }
 
index a41e3ba8cfaa416b50f639e8e7b184f0439ab1fb..4d113efa024c8439f4000a0fa59ed643f2489166 100644 (file)
@@ -4794,8 +4794,8 @@ do_more:
                                         " group:%d block:%d count:%lu failed"
                                         " with %d", block_group, bit, count,
                                         err);
-               }
-
+               } else
+                       EXT4_MB_GRP_CLEAR_TRIMMED(e4b.bd_info);
 
                ext4_lock_group(sb, block_group);
                mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
index 214461e42a05c8be0374224c81c2a549135b7014..04434ad3e8e0115785c653e1815bc9518563bbc7 100644 (file)
@@ -259,7 +259,7 @@ static unsigned int mmp_new_seq(void)
        u32 new_seq;
 
        do {
-               get_random_bytes(&new_seq, sizeof(u32));
+               new_seq = prandom_u32();
        } while (new_seq > EXT4_MMP_SEQ_MAX);
 
        return new_seq;
index d7d0c7b46ed40feec818701641fce792569dcec9..d488f80ee32df1137e91df0aed72bef2f61b49ac 100644 (file)
@@ -197,14 +197,15 @@ static void dump_completed_IO(struct inode *inode, struct list_head *head)
 static void ext4_add_complete_io(ext4_io_end_t *io_end)
 {
        struct ext4_inode_info *ei = EXT4_I(io_end->inode);
+       struct ext4_sb_info *sbi = EXT4_SB(io_end->inode->i_sb);
        struct workqueue_struct *wq;
        unsigned long flags;
 
        /* Only reserved conversions from writeback should enter here */
        WARN_ON(!(io_end->flag & EXT4_IO_END_UNWRITTEN));
-       WARN_ON(!io_end->handle);
+       WARN_ON(!io_end->handle && sbi->s_journal);
        spin_lock_irqsave(&ei->i_completed_io_lock, flags);
-       wq = EXT4_SB(io_end->inode->i_sb)->rsv_conversion_wq;
+       wq = sbi->rsv_conversion_wq;
        if (list_empty(&ei->i_rsv_conversion_list))
                queue_work(wq, &ei->i_rsv_conversion_work);
        list_add_tail(&io_end->list, &ei->i_rsv_conversion_list);
index 2c2e6cbc6bedc549938262e50be8e118d1cb6de7..c977f4e4e63be6c4cf3b477f628e4fc7d323d1f5 100644 (file)
@@ -411,20 +411,26 @@ static void ext4_handle_error(struct super_block *sb)
                        sb->s_id);
 }
 
+#define ext4_error_ratelimit(sb)                                       \
+               ___ratelimit(&(EXT4_SB(sb)->s_err_ratelimit_state),     \
+                            "EXT4-fs error")
+
 void __ext4_error(struct super_block *sb, const char *function,
                  unsigned int line, const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
 
-       va_start(args, fmt);
-       vaf.fmt = fmt;
-       vaf.va = &args;
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: comm %s: %pV\n",
-              sb->s_id, function, line, current->comm, &vaf);
-       va_end(args);
+       if (ext4_error_ratelimit(sb)) {
+               va_start(args, fmt);
+               vaf.fmt = fmt;
+               vaf.va = &args;
+               printk(KERN_CRIT
+                      "EXT4-fs error (device %s): %s:%d: comm %s: %pV\n",
+                      sb->s_id, function, line, current->comm, &vaf);
+               va_end(args);
+       }
        save_error_info(sb, function, line);
-
        ext4_handle_error(sb);
 }
 
@@ -438,22 +444,23 @@ void __ext4_error_inode(struct inode *inode, const char *function,
 
        es->s_last_error_ino = cpu_to_le32(inode->i_ino);
        es->s_last_error_block = cpu_to_le64(block);
+       if (ext4_error_ratelimit(inode->i_sb)) {
+               va_start(args, fmt);
+               vaf.fmt = fmt;
+               vaf.va = &args;
+               if (block)
+                       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: "
+                              "inode #%lu: block %llu: comm %s: %pV\n",
+                              inode->i_sb->s_id, function, line, inode->i_ino,
+                              block, current->comm, &vaf);
+               else
+                       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: "
+                              "inode #%lu: comm %s: %pV\n",
+                              inode->i_sb->s_id, function, line, inode->i_ino,
+                              current->comm, &vaf);
+               va_end(args);
+       }
        save_error_info(inode->i_sb, function, line);
-       va_start(args, fmt);
-       vaf.fmt = fmt;
-       vaf.va = &args;
-       if (block)
-               printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: "
-                      "inode #%lu: block %llu: comm %s: %pV\n",
-                      inode->i_sb->s_id, function, line, inode->i_ino,
-                      block, current->comm, &vaf);
-       else
-               printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: "
-                      "inode #%lu: comm %s: %pV\n",
-                      inode->i_sb->s_id, function, line, inode->i_ino,
-                      current->comm, &vaf);
-       va_end(args);
-
        ext4_handle_error(inode->i_sb);
 }
 
@@ -469,27 +476,28 @@ void __ext4_error_file(struct file *file, const char *function,
 
        es = EXT4_SB(inode->i_sb)->s_es;
        es->s_last_error_ino = cpu_to_le32(inode->i_ino);
+       if (ext4_error_ratelimit(inode->i_sb)) {
+               path = d_path(&(file->f_path), pathname, sizeof(pathname));
+               if (IS_ERR(path))
+                       path = "(unknown)";
+               va_start(args, fmt);
+               vaf.fmt = fmt;
+               vaf.va = &args;
+               if (block)
+                       printk(KERN_CRIT
+                              "EXT4-fs error (device %s): %s:%d: inode #%lu: "
+                              "block %llu: comm %s: path %s: %pV\n",
+                              inode->i_sb->s_id, function, line, inode->i_ino,
+                              block, current->comm, path, &vaf);
+               else
+                       printk(KERN_CRIT
+                              "EXT4-fs error (device %s): %s:%d: inode #%lu: "
+                              "comm %s: path %s: %pV\n",
+                              inode->i_sb->s_id, function, line, inode->i_ino,
+                              current->comm, path, &vaf);
+               va_end(args);
+       }
        save_error_info(inode->i_sb, function, line);
-       path = d_path(&(file->f_path), pathname, sizeof(pathname));
-       if (IS_ERR(path))
-               path = "(unknown)";
-       va_start(args, fmt);
-       vaf.fmt = fmt;
-       vaf.va = &args;
-       if (block)
-               printk(KERN_CRIT
-                      "EXT4-fs error (device %s): %s:%d: inode #%lu: "
-                      "block %llu: comm %s: path %s: %pV\n",
-                      inode->i_sb->s_id, function, line, inode->i_ino,
-                      block, current->comm, path, &vaf);
-       else
-               printk(KERN_CRIT
-                      "EXT4-fs error (device %s): %s:%d: inode #%lu: "
-                      "comm %s: path %s: %pV\n",
-                      inode->i_sb->s_id, function, line, inode->i_ino,
-                      current->comm, path, &vaf);
-       va_end(args);
-
        ext4_handle_error(inode->i_sb);
 }
 
@@ -543,11 +551,13 @@ void __ext4_std_error(struct super_block *sb, const char *function,
            (sb->s_flags & MS_RDONLY))
                return;
 
-       errstr = ext4_decode_error(sb, errno, nbuf);
-       printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
-              sb->s_id, function, line, errstr);
-       save_error_info(sb, function, line);
+       if (ext4_error_ratelimit(sb)) {
+               errstr = ext4_decode_error(sb, errno, nbuf);
+               printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
+                      sb->s_id, function, line, errstr);
+       }
 
+       save_error_info(sb, function, line);
        ext4_handle_error(sb);
 }
 
@@ -597,6 +607,9 @@ void __ext4_msg(struct super_block *sb,
        struct va_format vaf;
        va_list args;
 
+       if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
+               return;
+
        va_start(args, fmt);
        vaf.fmt = fmt;
        vaf.va = &args;
@@ -610,6 +623,10 @@ void __ext4_warning(struct super_block *sb, const char *function,
        struct va_format vaf;
        va_list args;
 
+       if (!___ratelimit(&(EXT4_SB(sb)->s_warning_ratelimit_state),
+                         "EXT4-fs warning"))
+               return;
+
        va_start(args, fmt);
        vaf.fmt = fmt;
        vaf.va = &args;
@@ -633,18 +650,20 @@ __acquires(bitlock)
        es->s_last_error_block = cpu_to_le64(block);
        __save_error_info(sb, function, line);
 
-       va_start(args, fmt);
-
-       vaf.fmt = fmt;
-       vaf.va = &args;
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u, ",
-              sb->s_id, function, line, grp);
-       if (ino)
-               printk(KERN_CONT "inode %lu: ", ino);
-       if (block)
-               printk(KERN_CONT "block %llu:", (unsigned long long) block);
-       printk(KERN_CONT "%pV\n", &vaf);
-       va_end(args);
+       if (ext4_error_ratelimit(sb)) {
+               va_start(args, fmt);
+               vaf.fmt = fmt;
+               vaf.va = &args;
+               printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u, ",
+                      sb->s_id, function, line, grp);
+               if (ino)
+                       printk(KERN_CONT "inode %lu: ", ino);
+               if (block)
+                       printk(KERN_CONT "block %llu:",
+                              (unsigned long long) block);
+               printk(KERN_CONT "%pV\n", &vaf);
+               va_end(args);
+       }
 
        if (test_opt(sb, ERRORS_CONT)) {
                ext4_commit_super(sb, 0);
@@ -2606,6 +2625,12 @@ EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
 EXT4_DEPRECATED_ATTR(max_writeback_mb_bump, 128);
 EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb);
 EXT4_ATTR(trigger_fs_error, 0200, NULL, trigger_test_error);
+EXT4_RW_ATTR_SBI_UI(err_ratelimit_interval_ms, s_err_ratelimit_state.interval);
+EXT4_RW_ATTR_SBI_UI(err_ratelimit_burst, s_err_ratelimit_state.burst);
+EXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.interval);
+EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
+EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
+EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
 
 static struct attribute *ext4_attrs[] = {
        ATTR_LIST(delayed_allocation_blocks),
@@ -2623,6 +2648,12 @@ static struct attribute *ext4_attrs[] = {
        ATTR_LIST(max_writeback_mb_bump),
        ATTR_LIST(extent_max_zeroout_kb),
        ATTR_LIST(trigger_fs_error),
+       ATTR_LIST(err_ratelimit_interval_ms),
+       ATTR_LIST(err_ratelimit_burst),
+       ATTR_LIST(warning_ratelimit_interval_ms),
+       ATTR_LIST(warning_ratelimit_burst),
+       ATTR_LIST(msg_ratelimit_interval_ms),
+       ATTR_LIST(msg_ratelimit_burst),
        NULL,
 };
 
@@ -3037,7 +3068,6 @@ static struct ext4_li_request *ext4_li_request_new(struct super_block *sb,
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        struct ext4_li_request *elr;
-       unsigned long rnd;
 
        elr = kzalloc(sizeof(*elr), GFP_KERNEL);
        if (!elr)
@@ -3052,10 +3082,8 @@ static struct ext4_li_request *ext4_li_request_new(struct super_block *sb,
         * spread the inode table initialization requests
         * better.
         */
-       get_random_bytes(&rnd, sizeof(rnd));
-       elr->lr_next_sched = jiffies + (unsigned long)rnd %
-                            (EXT4_DEF_LI_MAX_START_DELAY * HZ);
-
+       elr->lr_next_sched = jiffies + (prandom_u32() %
+                               (EXT4_DEF_LI_MAX_START_DELAY * HZ));
        return elr;
 }
 
@@ -4118,6 +4146,11 @@ no_journal:
        if (es->s_error_count)
                mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
 
+       /* Enable message ratelimiting. Default is 10 messages per 5 secs. */
+       ratelimit_state_init(&sbi->s_err_ratelimit_state, 5 * HZ, 10);
+       ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10);
+       ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);
+
        kfree(orig_data);
        return 0;
 
index 03e9bebba1989ef20263fbd1656959b764927b03..1423c4816a4783da4208b6d51fc33815b25597f4 100644 (file)
@@ -1352,6 +1352,7 @@ retry:
                                        new_extra_isize = s_min_extra_isize;
                                        kfree(is); is = NULL;
                                        kfree(bs); bs = NULL;
+                                       brelse(bh);
                                        goto retry;
                                }
                                error = -1;
index e66a8009aff16d66b1179bba0ffc3a266ff923f6..c8420f7e4db604da3663da61c5ae4556b6439783 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 1615df16cf4eb9ed5c5c4f20ee46c56bc00d3143..7119504159f17ba8fdde8317abee9eaf38583c71 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 c8423d6de6c3ee54341d5ea5c4eb7e20ca7255de..2a6ba06bee6fca0ffada9c155ca243466fcf1f62 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 453b50eaddec42f8e8461d9f96965e7d40ba746f..98236d0df3cae7ce7666a10dc7fc907590b873b0 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 4d83abdd5635273b3e0af9589eec83226be249ff..c8d6161bd682bd6cbd05247e2f0efc8274afe0b9 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 efc85b1377cce60c5d3e47853d60777f576c7a27..3c6136f98c737a8d0dc58f2a0cf41712e381ae92 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 25437280a2071b8970efe6e394edb97a4433acd8..db23ce1bd9031028390eb33016a05783950bc379 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 5de06947ba5ebf2e98caa8177085f7c191df4602..a1844244246f8e8e8da60da61e2aaabd2750e58e 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 e029a4cbff7db7b23af15628ca4d8c2cac5da491..c53d3a9547f9295408fa3cfe0d5abfb72023e29e 100644 (file)
@@ -513,8 +513,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
 
        if (!lockref_get_not_dead(&parent->d_lockref)) {
                nd->path.dentry = NULL; 
-               rcu_read_unlock();
-               return -ECHILD;
+               goto out;
        }
 
        /*
@@ -2435,6 +2434,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 38c1768b4142e6508a6805aaa1ff1067f8770381..3dece03f2fc8a4031db1bca96b9a8365bafd0312 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 c8e729deb4f711fc38349c2b883f29c7ecb6b407..059c01b67a7164a6d782139fb7b66b0d3976ad21 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 317d6fc2160ebe787714e0f0ac6cb5288b0527b7..910ed906eb82f0465eb9317f8c8d4c7640bc7e17 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 dc8f1ef665ce6830db7cbf43b327a5fe198fbe37..f994e750e0d1db9ebe53e7f51883bfa5c039afc4 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 5f38ea36e266751f700b806bd4b4dfb7c6ed0dcd..8513c598fabfbb7cc83e32357d4ff0f6ba8e087d 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 f36a30a9f2d15c5d3500002e87b4afa3e80d951a..105d6fa7c5149ab496cf614763a4c3145f52c74e 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 d9454fe5653f70af2175432d22e36cebcbaef5be..ee7237f99f54cd413dba6375dbc344084d0ece56 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;
 }
 
@@ -990,6 +994,14 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
        DECODE_TAIL;
 }
 
+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)
 {
@@ -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 3d0e15ae6f726311ef82b337e66bea91a26792e4..3c37b160dcad22c6db5dc469f52ff0b2b2f68f00 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 94b5f5d2bfedd94be68e7879a8cf8b40d0e3cde3..7eea63cada1d4a3ea5f9dd95401f83714689fcb0 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 3a44a648dae7709b1cd5431c426cf3b9e057e54a..3407b2c62b21bbf51981584f6645520048a2a57a 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 1485e38daaa38100278f56e710a8233338693fd5..03c8d747be48be2a14e93fed94355d42c5d7bd52 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 b701eaa482bfb4cc3d8b8d91c76884b91167fe11..51942d5abcecb43d8a081fefa830a97f52d05b76 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 737e15615b0490c40d002a315217033f5463d5b3..cca93b6fb9a9e841cc480308968771125fb7cd87 100644 (file)
@@ -174,22 +174,6 @@ static const struct inode_operations proc_link_inode_operations = {
        .follow_link    = proc_follow_link,
 };
 
-/*
- * 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 c805d5b69ba125a5ab57cee445520cc6b81c8522..a77d2b2991998187843271a714ab241adacfc0b5 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 49a7fff2e83a9906f39a685ddad0e98211dd155f..9ae46b87470dd9fe9fe6962c689abb4a7500e697 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 ccfd99bd1c5a627e41cae635f4527b8220af109e..5f9bc8a746c93dbfa99678b9a7d91c2a0999fdf9 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 abbe825d20ffa3dd4b4516ab57408803e1247f4f..fb52b548080da50cf2cece633c2585601485fc09 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 56123a6f462e7d9d84275d88f093c0926bf88576..678455d2d6839e9ab0df0909905d877aa5debb6a 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 16e8abb7709ba04fb1bcc1520573acf568e68eb9..72d29177998ebbf22e9888c9cd4cf43b3cc37e91 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 a290157265ef5cfb52dbdb6a9e950e9733462d16..1d641bb108d239f2476d862f0ce9e366c299762c 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 c70111ebefd44aaacf7248e10a5e80fecc06582a..b6fa8657dcbc51dcb904a7e5b018d474c94d9d83 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 110b0476f3b48a21e016dd8f4337476ddf37c3ce..4132520b4ff2cfbec2e49b5c0ebe67f8661dd748 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 41d108ecc9be305211a635bfdf7932ac52d91097..0cea9b9236d07c81d0cc46c0b22aeba334cc645d 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 af0b738025929b1c18ac884734131ea5e843e40c..1cb70a0b216844a136bf3b47ee6534d22496bb1c 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 3f6271d86abc48ba3132ccfdd5668e118799e249..ac22fe73b0adc241449c35e58faf535d510bb3ec 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 330073e29029950238da75d90fbd86a9dc8c881a..af0985321808e6e92b771b7d3ea63442e5fc8581 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 8ca62c28fe1249fd40d7b8e01612b1dedc5ca704..e5c9689062ba81fff5db08f50c2d39d53c4508d9 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 00f4dfc5f0884cb6d77920130883521b04635784..244b9fbfff7b299195585320328cfc7540e6887e 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 d1266516ed08494e93f4dc2ef00fe66be9216cb5..9e1bb79f7e6f09ea793d11d5e627483bddc048ea 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 52934a22f29665a00f082b24aa57ce915e875913..1da565cb50c3d0f1e652671dd7a81577b598ca2f 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 60553a9053ca82b7264862e8168b61df0ce7de89..202df6312d4e8517a63bb9ff10b385738e54ab6e 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 1760b7d108f66a55614102c43713ef315592374e..c609624e4b8a8cf88152310337c2533be7bec5cb 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 55d918fd2d862605beb85dae54c3a177b776381d..8727caba6882209ad62102c0148a09b64421626a 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 79b5da2acbe184353475f53ccb03793404bd3563..b94f93685093edb4f2d189238989d3024fffa246 100644 (file)
@@ -609,7 +609,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
        struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
        struct sysfs_open_file *of;
-       bool has_read, has_write;
+       bool has_read, has_write, has_mmap;
        int error = -EACCES;
 
        /* need attr_sd for attr and ops, its parent for kobj */
@@ -621,6 +621,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
 
                has_read = battr->read || battr->mmap;
                has_write = battr->write || battr->mmap;
+               has_mmap = battr->mmap;
        } else {
                const struct sysfs_ops *ops = sysfs_file_ops(attr_sd);
 
@@ -632,6 +633,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
 
                has_read = ops->show;
                has_write = ops->store;
+               has_mmap = false;
        }
 
        /* check perms and supported operations */
@@ -649,7 +651,23 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
        if (!of)
                goto err_out;
 
-       mutex_init(&of->mutex);
+       /*
+        * The following is done to give a different lockdep key to
+        * @of->mutex for files which implement mmap.  This is a rather
+        * crude way to avoid false positive lockdep warning around
+        * mm->mmap_sem - mmap nests @of->mutex under mm->mmap_sem and
+        * reading /sys/block/sda/trace/act_mask grabs sr_mutex, under
+        * which mm->mmap_sem nests, while holding @of->mutex.  As each
+        * open file has a separate mutex, it's okay as long as those don't
+        * happen on the same file.  At this point, we can't easily give
+        * each file a separate locking class.  Let's differentiate on
+        * whether the file has mmap or not for now.
+        */
+       if (has_mmap)
+               mutex_init(&of->mutex);
+       else
+               mutex_init(&of->mutex);
+
        of->sd = attr_sd;
        of->file = file;
 
index 0719e4db93f274de9af844f3ef66ce387b02a716..c21f4350666112c4222b33ad7c268e1d759d540c 100644 (file)
@@ -66,12 +66,14 @@ xfs-y                               += xfs_alloc.o \
                                   xfs_bmap_btree.o \
                                   xfs_btree.o \
                                   xfs_da_btree.o \
+                                  xfs_da_format.o \
                                   xfs_dir2.o \
                                   xfs_dir2_block.o \
                                   xfs_dir2_data.o \
                                   xfs_dir2_leaf.o \
                                   xfs_dir2_node.o \
                                   xfs_dir2_sf.o \
+                                  xfs_dquot_buf.o \
                                   xfs_ialloc.o \
                                   xfs_ialloc_btree.o \
                                   xfs_icreate_item.o \
@@ -103,7 +105,11 @@ xfs-$(CONFIG_XFS_QUOTA)            += xfs_dquot.o \
                                   xfs_qm_bhv.o \
                                   xfs_qm.o \
                                   xfs_quotaops.o
-xfs-$(CONFIG_XFS_RT)           += xfs_rtalloc.o
+
+# xfs_rtbitmap is shared with libxfs
+xfs-$(CONFIG_XFS_RT)           += xfs_rtalloc.o \
+                                  xfs_rtbitmap.o
+
 xfs-$(CONFIG_XFS_POSIX_ACL)    += xfs_acl.o
 xfs-$(CONFIG_PROC_FS)          += xfs_stats.o
 xfs-$(CONFIG_SYSCTL)           += xfs_sysctl.o
index a02cfb9e3bcea43d49a033f313387fa217aa789c..66a36befc5c0723d217bdd5aa1a61b1b610e42a4 100644 (file)
@@ -62,17 +62,6 @@ kmem_alloc(size_t size, xfs_km_flags_t flags)
        } while (1);
 }
 
-void *
-kmem_zalloc(size_t size, xfs_km_flags_t flags)
-{
-       void    *ptr;
-
-       ptr = kmem_alloc(size, flags);
-       if (ptr)
-               memset((char *)ptr, 0, (int)size);
-       return ptr;
-}
-
 void *
 kmem_zalloc_large(size_t size, xfs_km_flags_t flags)
 {
@@ -128,14 +117,3 @@ kmem_zone_alloc(kmem_zone_t *zone, xfs_km_flags_t flags)
                congestion_wait(BLK_RW_ASYNC, HZ/50);
        } while (1);
 }
-
-void *
-kmem_zone_zalloc(kmem_zone_t *zone, xfs_km_flags_t flags)
-{
-       void    *ptr;
-
-       ptr = kmem_zone_alloc(zone, flags);
-       if (ptr)
-               memset((char *)ptr, 0, kmem_cache_size(zone));
-       return ptr;
-}
index 3a7371cab508a7ffea0fc9441d5319026ce089e2..64db0e53edeac506a23ea7b295e196528b435035 100644 (file)
@@ -32,6 +32,7 @@ typedef unsigned __bitwise xfs_km_flags_t;
 #define KM_NOSLEEP     ((__force xfs_km_flags_t)0x0002u)
 #define KM_NOFS                ((__force xfs_km_flags_t)0x0004u)
 #define KM_MAYFAIL     ((__force xfs_km_flags_t)0x0008u)
+#define KM_ZERO                ((__force xfs_km_flags_t)0x0010u)
 
 /*
  * We use a special process flag to avoid recursive callbacks into
@@ -43,7 +44,7 @@ kmem_flags_convert(xfs_km_flags_t flags)
 {
        gfp_t   lflags;
 
-       BUG_ON(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL));
+       BUG_ON(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL|KM_ZERO));
 
        if (flags & KM_NOSLEEP) {
                lflags = GFP_ATOMIC | __GFP_NOWARN;
@@ -52,11 +53,14 @@ kmem_flags_convert(xfs_km_flags_t flags)
                if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS))
                        lflags &= ~__GFP_FS;
        }
+
+       if (flags & KM_ZERO)
+               lflags |= __GFP_ZERO;
+
        return lflags;
 }
 
 extern void *kmem_alloc(size_t, xfs_km_flags_t);
-extern void *kmem_zalloc(size_t, xfs_km_flags_t);
 extern void *kmem_zalloc_large(size_t size, xfs_km_flags_t);
 extern void *kmem_realloc(const void *, size_t, size_t, xfs_km_flags_t);
 extern void  kmem_free(const void *);
@@ -64,6 +68,12 @@ extern void  kmem_free(const void *);
 
 extern void *kmem_zalloc_greedy(size_t *, size_t, size_t);
 
+static inline void *
+kmem_zalloc(size_t size, xfs_km_flags_t flags)
+{
+       return kmem_alloc(size, flags | KM_ZERO);
+}
+
 /*
  * Zone interfaces
  */
@@ -102,6 +112,11 @@ kmem_zone_destroy(kmem_zone_t *zone)
 }
 
 extern void *kmem_zone_alloc(kmem_zone_t *, xfs_km_flags_t);
-extern void *kmem_zone_zalloc(kmem_zone_t *, xfs_km_flags_t);
+
+static inline void *
+kmem_zone_zalloc(kmem_zone_t *zone, xfs_km_flags_t flags)
+{
+       return kmem_zone_alloc(zone, flags | KM_ZERO);
+}
 
 #endif /* __XFS_SUPPORT_KMEM_H__ */
index 0e2f37efedd0547a05b5bec4c0109db83192bb72..370eb3e121d1b2f292c65ddabd660ad08ae65537 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
+#include "xfs_format.h"
 #include "xfs_log_format.h"
 #include "xfs_trans_resv.h"
-#include "xfs_acl.h"
-#include "xfs_attr.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_inode.h"
 #include "xfs_ag.h"
 #include "xfs_sb.h"
 #include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_acl.h"
+#include "xfs_attr.h"
 #include "xfs_trace.h"
 #include <linux/slab.h>
 #include <linux/xattr.h>
index 1cb740afd674e8fd3f5ce0a3f47bfc00f8b36471..3fc109819c34eb7a42973d7640525fefe66cc08e 100644 (file)
@@ -128,8 +128,6 @@ typedef struct xfs_agf {
 extern int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
                        xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
 
-extern const struct xfs_buf_ops xfs_agf_buf_ops;
-
 /*
  * Size of the unlinked inode hash table in the agi.
  */
@@ -191,8 +189,6 @@ typedef struct xfs_agi {
 extern int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
                                xfs_agnumber_t agno, struct xfs_buf **bpp);
 
-extern const struct xfs_buf_ops xfs_agi_buf_ops;
-
 /*
  * The third a.g. block contains the a.g. freelist, an array
  * of block pointers to blocks owned by the allocation btree code.
index 5a1393f5e020739a648612002f7ae9a3f704d032..9eab2dfdcbb54cc1337f4d6f248c808fcb2cc426 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
+#include "xfs_alloc_btree.h"
 #include "xfs_alloc.h"
 #include "xfs_extent_busy.h"
 #include "xfs_error.h"
 #include "xfs_cksum.h"
 #include "xfs_trace.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
+#include "xfs_log.h"
 
 struct workqueue_struct *xfs_alloc_wq;
 
@@ -2294,6 +2294,8 @@ xfs_read_agf(
 {
        int             error;
 
+       trace_xfs_read_agf(mp, agno);
+
        ASSERT(agno != NULLAGNUMBER);
        error = xfs_trans_read_buf(
                        mp, tp, mp->m_ddev_targp,
@@ -2324,8 +2326,9 @@ xfs_alloc_read_agf(
        struct xfs_perag        *pag;           /* per allocation group data */
        int                     error;
 
-       ASSERT(agno != NULLAGNUMBER);
+       trace_xfs_alloc_read_agf(mp, agno);
 
+       ASSERT(agno != NULLAGNUMBER);
        error = xfs_read_agf(mp, tp, agno,
                        (flags & XFS_ALLOC_FLAG_TRYLOCK) ? XBF_TRYLOCK : 0,
                        bpp);
index 99d0a61015587f7b8400bb8519bec6717b91c1cf..feacb061bab78bb3492ee0ff37eed5cc0fb95894 100644 (file)
@@ -231,7 +231,4 @@ xfs_alloc_get_rec(
        xfs_extlen_t            *len,   /* output: length of extent */
        int                     *stat); /* output: success/failure */
 
-extern const struct xfs_buf_ops xfs_agf_buf_ops;
-extern const struct xfs_buf_ops xfs_agfl_buf_ops;
-
 #endif /* __XFS_ALLOC_H__ */
index cafc90251d1993e76c10d0b6c6dae9d875dcd21e..13085429e5234783c93e40c3a31492ec7d560428 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
 #include "xfs_btree.h"
+#include "xfs_alloc_btree.h"
 #include "xfs_alloc.h"
 #include "xfs_extent_busy.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
+#include "xfs_trans.h"
 
 
 STATIC struct xfs_btree_cur *
index e3a3f7424192ecd15a21169d94817e507cc1e6af..45e189e7e81c31b18a0de124cd4d256407d2a120 100644 (file)
@@ -26,39 +26,6 @@ struct xfs_buf;
 struct xfs_btree_cur;
 struct xfs_mount;
 
-/*
- * There are two on-disk btrees, one sorted by blockno and one sorted
- * by blockcount and blockno.  All blocks look the same to make the code
- * simpler; if we have time later, we'll make the optimizations.
- */
-#define        XFS_ABTB_MAGIC          0x41425442      /* 'ABTB' for bno tree */
-#define        XFS_ABTB_CRC_MAGIC      0x41423342      /* 'AB3B' */
-#define        XFS_ABTC_MAGIC          0x41425443      /* 'ABTC' for cnt tree */
-#define        XFS_ABTC_CRC_MAGIC      0x41423343      /* 'AB3C' */
-
-/*
- * Data record/key structure
- */
-typedef struct xfs_alloc_rec {
-       __be32          ar_startblock;  /* starting block number */
-       __be32          ar_blockcount;  /* count of free blocks */
-} xfs_alloc_rec_t, xfs_alloc_key_t;
-
-typedef struct xfs_alloc_rec_incore {
-       xfs_agblock_t   ar_startblock;  /* starting block number */
-       xfs_extlen_t    ar_blockcount;  /* count of free blocks */
-} xfs_alloc_rec_incore_t;
-
-/* btree pointer type */
-typedef __be32 xfs_alloc_ptr_t;
-
-/*
- * Block numbers in the AG:
- * SB is sector 0, AGF is sector 1, AGI is sector 2, AGFL is sector 3.
- */
-#define        XFS_BNO_BLOCK(mp)       ((xfs_agblock_t)(XFS_AGFL_BLOCK(mp) + 1))
-#define        XFS_CNT_BLOCK(mp)       ((xfs_agblock_t)(XFS_BNO_BLOCK(mp) + 1))
-
 /*
  * Btree block header size depends on a superblock flag.
  */
@@ -95,6 +62,4 @@ extern struct xfs_btree_cur *xfs_allocbt_init_cursor(struct xfs_mount *,
                xfs_agnumber_t, xfs_btnum_t);
 extern int xfs_allocbt_maxrecs(struct xfs_mount *, int, int);
 
-extern const struct xfs_buf_ops xfs_allocbt_buf_ops;
-
 #endif /* __XFS_ALLOC_BTREE_H__ */
index e51e581454e93113c7ead8d81136ab82bd29da94..71c8c9d2b8826a0ed22d886fb3327d2ff0784fc3 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
-#include "xfs_log.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_trans.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_alloc.h"
 #include "xfs_error.h"
@@ -31,6 +32,8 @@
 #include "xfs_trace.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_dinode.h"
 #include <linux/aio.h>
 #include <linux/gfp.h>
 #include <linux/mpage.h>
@@ -333,7 +336,7 @@ xfs_map_blocks(
 
        if (type == XFS_IO_DELALLOC &&
            (!nimaps || isnullstartblock(imap->br_startblock))) {
-               error = xfs_iomap_write_allocate(ip, offset, count, imap);
+               error = xfs_iomap_write_allocate(ip, offset, imap);
                if (!error)
                        trace_xfs_map_blocks_alloc(ip, offset, count, type, imap);
                return -XFS_ERROR(error);
@@ -1569,8 +1572,7 @@ xfs_vm_write_begin(
 
        ASSERT(len <= PAGE_CACHE_SIZE);
 
-       page = grab_cache_page_write_begin(mapping, index,
-                                          flags | AOP_FLAG_NOFS);
+       page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
                return -ENOMEM;
 
index ddcf2267ffa6fdf1bcf33cf7439c6b379472c2b4..b86127072ac3c2b9bd201b34cf208c3f1735cd68 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_attr_sf.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_alloc.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_attr_remote.h"
@@ -41,6 +42,7 @@
 #include "xfs_quota.h"
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
+#include "xfs_dinode.h"
 
 /*
  * xfs_attr.c
index bb24b07cbedb3d46a6286e8e169a734ad9fa3baa..09480c57f06900ccb5fdba4817d561908f0deb1e 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
+#include "xfs_inode.h"
 #include "xfs_alloc.h"
-#include "xfs_btree.h"
 #include "xfs_attr_remote.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
 #include "xfs_attr.h"
@@ -41,7 +39,8 @@
 #include "xfs_error.h"
 #include "xfs_quota.h"
 #include "xfs_trace.h"
-#include "xfs_trans_priv.h"
+#include "xfs_dinode.h"
+#include "xfs_dir2.h"
 
 /*
  * Look at all the extents for this logical region,
@@ -232,13 +231,13 @@ xfs_attr3_node_inactive(
        }
 
        node = bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&ichdr, node);
+       dp->d_ops->node_hdr_from_disk(&ichdr, node);
        parent_blkno = bp->b_bn;
        if (!ichdr.count) {
                xfs_trans_brelse(*trans, bp);
                return 0;
        }
-       btree = xfs_da3_node_tree_p(node);
+       btree = dp->d_ops->node_tree_p(node);
        child_fsb = be32_to_cpu(btree[0].before);
        xfs_trans_brelse(*trans, bp);   /* no locks for later trans */
 
index 86db20a9cc02b5df2dd7ecb3c44eca39d7498dd7..7b126f46a2f99689cf0bee0efba6c026c0e23186 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_alloc.h"
-#include "xfs_btree.h"
-#include "xfs_attr_sf.h"
-#include "xfs_attr_remote.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_bmap.h"
+#include "xfs_attr_sf.h"
+#include "xfs_attr_remote.h"
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_buf_item.h"
 #include "xfs_cksum.h"
+#include "xfs_dinode.h"
+#include "xfs_dir2.h"
 
 
 /*
@@ -918,8 +917,8 @@ xfs_attr3_leaf_to_node(
        if (error)
                goto out;
        node = bp1->b_addr;
-       xfs_da3_node_hdr_from_disk(&icnodehdr, node);
-       btree = xfs_da3_node_tree_p(node);
+       dp->d_ops->node_hdr_from_disk(&icnodehdr, node);
+       btree = dp->d_ops->node_tree_p(node);
 
        leaf = bp2->b_addr;
        xfs_attr3_leaf_hdr_from_disk(&icleafhdr, leaf);
@@ -929,7 +928,7 @@ xfs_attr3_leaf_to_node(
        btree[0].hashval = entries[icleafhdr.count - 1].hashval;
        btree[0].before = cpu_to_be32(blkno);
        icnodehdr.count = 1;
-       xfs_da3_node_hdr_to_disk(node, &icnodehdr);
+       dp->d_ops->node_hdr_to_disk(node, &icnodehdr);
        xfs_trans_log_buf(args->trans, bp1, 0, XFS_LBSIZE(mp) - 1);
        error = 0;
 out:
index c1022138c7e6f3261819a0c52618b6bb1a9cf34f..3ec5ec0b86789004428591b824d982a16262a829 100644 (file)
 #ifndef __XFS_ATTR_LEAF_H__
 #define        __XFS_ATTR_LEAF_H__
 
-/*
- * Attribute storage layout, internal structure, access macros, etc.
- *
- * Attribute lists are structured around Btrees where all the data
- * elements are in the leaf nodes.  Attribute names are hashed into an int,
- * then that int is used as the index into the Btree.  Since the hashval
- * of an attribute name may not be unique, we may have duplicate keys.  The
- * internal links in the Btree are logical block offsets into the file.
- */
-
 struct attrlist;
 struct attrlist_cursor_kern;
 struct xfs_attr_list_context;
@@ -38,226 +28,6 @@ struct xfs_da_state_blk;
 struct xfs_inode;
 struct xfs_trans;
 
-/*========================================================================
- * Attribute structure when equal to XFS_LBSIZE(mp) bytes.
- *========================================================================*/
-
-/*
- * This is the structure of the leaf nodes in the Btree.
- *
- * Struct leaf_entry's are packed from the top.  Name/values grow from the
- * bottom but are not packed.  The freemap contains run-length-encoded entries
- * for the free bytes after the leaf_entry's, but only the N largest such,
- * smaller runs are dropped.  When the freemap doesn't show enough space
- * for an allocation, we compact the name/value area and try again.  If we
- * still don't have enough space, then we have to split the block.  The
- * name/value structs (both local and remote versions) must be 32bit aligned.
- *
- * Since we have duplicate hash keys, for each key that matches, compare
- * the actual name string.  The root and intermediate node search always
- * takes the first-in-the-block key match found, so we should only have
- * to work "forw"ard.  If none matches, continue with the "forw"ard leaf
- * nodes until the hash key changes or the attribute name is found.
- *
- * We store the fact that an attribute is a ROOT/USER/SECURE attribute in
- * the leaf_entry.  The namespaces are independent only because we also look
- * at the namespace bit when we are looking for a matching attribute name.
- *
- * We also store an "incomplete" bit in the leaf_entry.  It shows that an
- * attribute is in the middle of being created and should not be shown to
- * the user if we crash during the time that the bit is set.  We clear the
- * bit when we have finished setting up the attribute.  We do this because
- * we cannot create some large attributes inside a single transaction, and we
- * need some indication that we weren't finished if we crash in the middle.
- */
-#define XFS_ATTR_LEAF_MAPSIZE  3       /* how many freespace slots */
-
-typedef struct xfs_attr_leaf_map {     /* RLE map of free bytes */
-       __be16  base;                     /* base of free region */
-       __be16  size;                     /* length of free region */
-} xfs_attr_leaf_map_t;
-
-typedef struct xfs_attr_leaf_hdr {     /* constant-structure header block */
-       xfs_da_blkinfo_t info;          /* block type, links, etc. */
-       __be16  count;                  /* count of active leaf_entry's */
-       __be16  usedbytes;              /* num bytes of names/values stored */
-       __be16  firstused;              /* first used byte in name area */
-       __u8    holes;                  /* != 0 if blk needs compaction */
-       __u8    pad1;
-       xfs_attr_leaf_map_t freemap[XFS_ATTR_LEAF_MAPSIZE];
-                                       /* N largest free regions */
-} xfs_attr_leaf_hdr_t;
-
-typedef struct xfs_attr_leaf_entry {   /* sorted on key, not name */
-       __be32  hashval;                /* hash value of name */
-       __be16  nameidx;                /* index into buffer of name/value */
-       __u8    flags;                  /* LOCAL/ROOT/SECURE/INCOMPLETE flag */
-       __u8    pad2;                   /* unused pad byte */
-} xfs_attr_leaf_entry_t;
-
-typedef struct xfs_attr_leaf_name_local {
-       __be16  valuelen;               /* number of bytes in value */
-       __u8    namelen;                /* length of name bytes */
-       __u8    nameval[1];             /* name/value bytes */
-} xfs_attr_leaf_name_local_t;
-
-typedef struct xfs_attr_leaf_name_remote {
-       __be32  valueblk;               /* block number of value bytes */
-       __be32  valuelen;               /* number of bytes in value */
-       __u8    namelen;                /* length of name bytes */
-       __u8    name[1];                /* name bytes */
-} xfs_attr_leaf_name_remote_t;
-
-typedef struct xfs_attr_leafblock {
-       xfs_attr_leaf_hdr_t     hdr;    /* constant-structure header block */
-       xfs_attr_leaf_entry_t   entries[1];     /* sorted on key, not name */
-       xfs_attr_leaf_name_local_t namelist;    /* grows from bottom of buf */
-       xfs_attr_leaf_name_remote_t valuelist;  /* grows from bottom of buf */
-} xfs_attr_leafblock_t;
-
-/*
- * CRC enabled leaf structures. Called "version 3" structures to match the
- * version number of the directory and dablk structures for this feature, and
- * attr2 is already taken by the variable inode attribute fork size feature.
- */
-struct xfs_attr3_leaf_hdr {
-       struct xfs_da3_blkinfo  info;
-       __be16                  count;
-       __be16                  usedbytes;
-       __be16                  firstused;
-       __u8                    holes;
-       __u8                    pad1;
-       struct xfs_attr_leaf_map freemap[XFS_ATTR_LEAF_MAPSIZE];
-       __be32                  pad2;           /* 64 bit alignment */
-};
-
-#define XFS_ATTR3_LEAF_CRC_OFF (offsetof(struct xfs_attr3_leaf_hdr, info.crc))
-
-struct xfs_attr3_leafblock {
-       struct xfs_attr3_leaf_hdr       hdr;
-       struct xfs_attr_leaf_entry      entries[1];
-
-       /*
-        * The rest of the block contains the following structures after the
-        * leaf entries, growing from the bottom up. The variables are never
-        * referenced, the locations accessed purely from helper functions.
-        *
-        * struct xfs_attr_leaf_name_local
-        * struct xfs_attr_leaf_name_remote
-        */
-};
-
-/*
- * incore, neutral version of the attribute leaf header
- */
-struct xfs_attr3_icleaf_hdr {
-       __uint32_t      forw;
-       __uint32_t      back;
-       __uint16_t      magic;
-       __uint16_t      count;
-       __uint16_t      usedbytes;
-       __uint16_t      firstused;
-       __u8            holes;
-       struct {
-               __uint16_t      base;
-               __uint16_t      size;
-       } freemap[XFS_ATTR_LEAF_MAPSIZE];
-};
-
-/*
- * Flags used in the leaf_entry[i].flags field.
- * NOTE: the INCOMPLETE bit must not collide with the flags bits specified
- * on the system call, they are "or"ed together for various operations.
- */
-#define        XFS_ATTR_LOCAL_BIT      0       /* attr is stored locally */
-#define        XFS_ATTR_ROOT_BIT       1       /* limit access to trusted attrs */
-#define        XFS_ATTR_SECURE_BIT     2       /* limit access to secure attrs */
-#define        XFS_ATTR_INCOMPLETE_BIT 7       /* attr in middle of create/delete */
-#define XFS_ATTR_LOCAL         (1 << XFS_ATTR_LOCAL_BIT)
-#define XFS_ATTR_ROOT          (1 << XFS_ATTR_ROOT_BIT)
-#define XFS_ATTR_SECURE                (1 << XFS_ATTR_SECURE_BIT)
-#define XFS_ATTR_INCOMPLETE    (1 << XFS_ATTR_INCOMPLETE_BIT)
-
-/*
- * Conversion macros for converting namespace bits from argument flags
- * to ondisk flags.
- */
-#define XFS_ATTR_NSP_ARGS_MASK         (ATTR_ROOT | ATTR_SECURE)
-#define XFS_ATTR_NSP_ONDISK_MASK       (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
-#define XFS_ATTR_NSP_ONDISK(flags)     ((flags) & XFS_ATTR_NSP_ONDISK_MASK)
-#define XFS_ATTR_NSP_ARGS(flags)       ((flags) & XFS_ATTR_NSP_ARGS_MASK)
-#define XFS_ATTR_NSP_ARGS_TO_ONDISK(x) (((x) & ATTR_ROOT ? XFS_ATTR_ROOT : 0) |\
-                                        ((x) & ATTR_SECURE ? XFS_ATTR_SECURE : 0))
-#define XFS_ATTR_NSP_ONDISK_TO_ARGS(x) (((x) & XFS_ATTR_ROOT ? ATTR_ROOT : 0) |\
-                                        ((x) & XFS_ATTR_SECURE ? ATTR_SECURE : 0))
-
-/*
- * Alignment for namelist and valuelist entries (since they are mixed
- * there can be only one alignment value)
- */
-#define        XFS_ATTR_LEAF_NAME_ALIGN        ((uint)sizeof(xfs_dablk_t))
-
-static inline int
-xfs_attr3_leaf_hdr_size(struct xfs_attr_leafblock *leafp)
-{
-       if (leafp->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC))
-               return sizeof(struct xfs_attr3_leaf_hdr);
-       return sizeof(struct xfs_attr_leaf_hdr);
-}
-
-static inline struct xfs_attr_leaf_entry *
-xfs_attr3_leaf_entryp(xfs_attr_leafblock_t *leafp)
-{
-       if (leafp->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC))
-               return &((struct xfs_attr3_leafblock *)leafp)->entries[0];
-       return &leafp->entries[0];
-}
-
-/*
- * Cast typed pointers for "local" and "remote" name/value structs.
- */
-static inline char *
-xfs_attr3_leaf_name(xfs_attr_leafblock_t *leafp, int idx)
-{
-       struct xfs_attr_leaf_entry *entries = xfs_attr3_leaf_entryp(leafp);
-
-       return &((char *)leafp)[be16_to_cpu(entries[idx].nameidx)];
-}
-
-static inline xfs_attr_leaf_name_remote_t *
-xfs_attr3_leaf_name_remote(xfs_attr_leafblock_t *leafp, int idx)
-{
-       return (xfs_attr_leaf_name_remote_t *)xfs_attr3_leaf_name(leafp, idx);
-}
-
-static inline xfs_attr_leaf_name_local_t *
-xfs_attr3_leaf_name_local(xfs_attr_leafblock_t *leafp, int idx)
-{
-       return (xfs_attr_leaf_name_local_t *)xfs_attr3_leaf_name(leafp, idx);
-}
-
-/*
- * Calculate total bytes used (including trailing pad for alignment) for
- * a "local" name/value structure, a "remote" name/value structure, and
- * a pointer which might be either.
- */
-static inline int xfs_attr_leaf_entsize_remote(int nlen)
-{
-       return ((uint)sizeof(xfs_attr_leaf_name_remote_t) - 1 + (nlen) + \
-               XFS_ATTR_LEAF_NAME_ALIGN - 1) & ~(XFS_ATTR_LEAF_NAME_ALIGN - 1);
-}
-
-static inline int xfs_attr_leaf_entsize_local(int nlen, int vlen)
-{
-       return ((uint)sizeof(xfs_attr_leaf_name_local_t) - 1 + (nlen) + (vlen) +
-               XFS_ATTR_LEAF_NAME_ALIGN - 1) & ~(XFS_ATTR_LEAF_NAME_ALIGN - 1);
-}
-
-static inline int xfs_attr_leaf_entsize_local_max(int bsize)
-{
-       return (((bsize) >> 1) + ((bsize) >> 2));
-}
-
 /*
  * Used to keep a list of "remote value" extents when unlinking an inode.
  */
@@ -336,6 +106,4 @@ void        xfs_attr3_leaf_hdr_from_disk(struct xfs_attr3_icleaf_hdr *to,
 void   xfs_attr3_leaf_hdr_to_disk(struct xfs_attr_leafblock *to,
                                   struct xfs_attr3_icleaf_hdr *from);
 
-extern const struct xfs_buf_ops xfs_attr3_leaf_buf_ops;
-
 #endif /* __XFS_ATTR_LEAF_H__ */
index cbc80d4851772a9df2cb51abbd83e5ef799c6870..2d174b12815345b5aafacb7046caf6a0bc98d147 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_alloc.h"
-#include "xfs_btree.h"
-#include "xfs_attr_sf.h"
-#include "xfs_attr_remote.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
 #include "xfs_attr.h"
+#include "xfs_attr_sf.h"
+#include "xfs_attr_remote.h"
 #include "xfs_attr_leaf.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_buf_item.h"
 #include "xfs_cksum.h"
+#include "xfs_dinode.h"
+#include "xfs_dir2.h"
 
 STATIC int
 xfs_attr_shortform_compare(const void *a, const void *b)
@@ -229,6 +227,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
        struct xfs_da_node_entry *btree;
        int error, i;
        struct xfs_buf *bp;
+       struct xfs_inode        *dp = context->dp;
 
        trace_xfs_attr_node_list(context);
 
@@ -242,7 +241,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
         */
        bp = NULL;
        if (cursor->blkno > 0) {
-               error = xfs_da3_node_read(NULL, context->dp, cursor->blkno, -1,
+               error = xfs_da3_node_read(NULL, dp, cursor->blkno, -1,
                                              &bp, XFS_ATTR_FORK);
                if ((error != 0) && (error != EFSCORRUPTED))
                        return(error);
@@ -292,7 +291,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                for (;;) {
                        __uint16_t magic;
 
-                       error = xfs_da3_node_read(NULL, context->dp,
+                       error = xfs_da3_node_read(NULL, dp,
                                                      cursor->blkno, -1, &bp,
                                                      XFS_ATTR_FORK);
                        if (error)
@@ -312,8 +311,8 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                                return XFS_ERROR(EFSCORRUPTED);
                        }
 
-                       xfs_da3_node_hdr_from_disk(&nodehdr, node);
-                       btree = xfs_da3_node_tree_p(node);
+                       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+                       btree = dp->d_ops->node_tree_p(node);
                        for (i = 0; i < nodehdr.count; btree++, i++) {
                                if (cursor->hashval
                                                <= be32_to_cpu(btree->hashval)) {
@@ -349,8 +348,7 @@ xfs_attr_node_list(xfs_attr_list_context_t *context)
                        break;
                cursor->blkno = leafhdr.forw;
                xfs_trans_brelse(NULL, bp);
-               error = xfs_attr3_leaf_read(NULL, context->dp, cursor->blkno, -1,
-                                          &bp);
+               error = xfs_attr3_leaf_read(NULL, dp, cursor->blkno, -1, &bp);
                if (error)
                        return error;
        }
index 712a502de619b097df202f744ba481bd3471847d..739e0a52dedadea67eb595f4b03619ce3e3383a3 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_error.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_alloc.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
@@ -42,6 +41,7 @@
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
 #include "xfs_buf_item.h"
+#include "xfs_error.h"
 
 #define ATTR_RMTVALUE_MAPSIZE  1       /* # of map entries at once */
 
index 92a8fd7977cc2b48649ed3e5c22344fe83fcd8c1..5a9acfa156d7af59855fde90fb5e6d8721ea289b 100644 (file)
 #ifndef __XFS_ATTR_REMOTE_H__
 #define        __XFS_ATTR_REMOTE_H__
 
-#define XFS_ATTR3_RMT_MAGIC    0x5841524d      /* XARM */
-
-/*
- * There is one of these headers per filesystem block in a remote attribute.
- * This is done to ensure there is a 1:1 mapping between the attribute value
- * length and the number of blocks needed to store the attribute. This makes the
- * verification of a buffer a little more complex, but greatly simplifies the
- * allocation, reading and writing of these attributes as we don't have to guess
- * the number of blocks needed to store the attribute data.
- */
-struct xfs_attr3_rmt_hdr {
-       __be32  rm_magic;
-       __be32  rm_offset;
-       __be32  rm_bytes;
-       __be32  rm_crc;
-       uuid_t  rm_uuid;
-       __be64  rm_owner;
-       __be64  rm_blkno;
-       __be64  rm_lsn;
-};
-
-#define XFS_ATTR3_RMT_CRC_OFF  offsetof(struct xfs_attr3_rmt_hdr, rm_crc)
-
-#define XFS_ATTR3_RMT_BUF_SPACE(mp, bufsize)   \
-       ((bufsize) - (xfs_sb_version_hascrc(&(mp)->m_sb) ? \
-                       sizeof(struct xfs_attr3_rmt_hdr) : 0))
-
-extern const struct xfs_buf_ops xfs_attr3_rmt_buf_ops;
-
 int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen);
 
 int xfs_attr_rmtval_get(struct xfs_da_args *args);
index 48228848f5ae3e2e6f52900b6d0ea7215274a36d..0e8885a596463e9e6d249974d271a234c420057c 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
+#include "xfs_log_format.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_buf_item.h"
 
 /*
  * XFS bit manipulation routines, used in non-realtime code.
index f47e65c30be6ddde5caa276d3262540d603d07dc..3ef11b22e7505c380feb6597113d5b150f7b1afb 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
-#include "xfs_mount.h"
-#include "xfs_itable.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_extfree_item.h"
 #include "xfs_alloc.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_rtalloc.h"
 #include "xfs_error.h"
-#include "xfs_attr_leaf.h"
 #include "xfs_quota.h"
 #include "xfs_trans_space.h"
 #include "xfs_buf_item.h"
-#include "xfs_filestream.h"
 #include "xfs_trace.h"
 #include "xfs_symlink.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_dinode.h"
+#include "xfs_filestream.h"
 
 
 kmem_zone_t            *xfs_bmap_free_item_zone;
@@ -1139,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);
 
@@ -1149,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.
@@ -1171,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) {
@@ -1193,7 +1193,7 @@ xfs_bmap_add_attrfork(
        default:
                ASSERT(0);
                error = XFS_ERROR(EINVAL);
-               goto error1;
+               goto trans_cancel;
        }
 
        ASSERT(ip->i_afp == NULL);
@@ -1221,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;
@@ -1244,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;
 }
 
@@ -1482,7 +1484,7 @@ xfs_bmap_search_extents(
                xfs_alert_tag(ip->i_mount, XFS_PTAG_FSBLOCK_ZERO,
                                "Access to block zero in inode %llu "
                                "start_block: %llx start_off: %llx "
-                               "blkcnt: %llx extent-state: %x lastx: %x\n",
+                               "blkcnt: %llx extent-state: %x lastx: %x",
                        (unsigned long long)ip->i_ino,
                        (unsigned long long)gotp->br_startblock,
                        (unsigned long long)gotp->br_startoff,
index bb8de8e399c4b351effa08e6669e000438751d37..706bc3f777cb390cacd78988b8b8ebe1a63799a6 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_alloc.h"
 #include "xfs_btree.h"
-#include "xfs_itable.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_bmap.h"
 #include "xfs_error.h"
 #include "xfs_quota.h"
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
+#include "xfs_dinode.h"
 
 /*
  * Determine the extent state.
index e367461a638e5b65ffdedc77bed6121dc1150161..6e42e1e50b89394e2c0a0a003c339ac03d127445 100644 (file)
 #ifndef __XFS_BMAP_BTREE_H__
 #define __XFS_BMAP_BTREE_H__
 
-#define XFS_BMAP_MAGIC         0x424d4150      /* 'BMAP' */
-#define XFS_BMAP_CRC_MAGIC     0x424d4133      /* 'BMA3' */
-
 struct xfs_btree_cur;
 struct xfs_btree_block;
 struct xfs_mount;
 struct xfs_inode;
 struct xfs_trans;
 
-/*
- * Bmap root header, on-disk form only.
- */
-typedef struct xfs_bmdr_block {
-       __be16          bb_level;       /* 0 is a leaf */
-       __be16          bb_numrecs;     /* current # of data records */
-} xfs_bmdr_block_t;
-
-/*
- * Bmap btree record and extent descriptor.
- *  l0:63 is an extent flag (value 1 indicates non-normal).
- *  l0:9-62 are startoff.
- *  l0:0-8 and l1:21-63 are startblock.
- *  l1:0-20 are blockcount.
- */
-#define BMBT_EXNTFLAG_BITLEN   1
-#define BMBT_STARTOFF_BITLEN   54
-#define BMBT_STARTBLOCK_BITLEN 52
-#define BMBT_BLOCKCOUNT_BITLEN 21
-
-typedef struct xfs_bmbt_rec {
-       __be64                  l0, l1;
-} xfs_bmbt_rec_t;
-
-typedef __uint64_t     xfs_bmbt_rec_base_t;    /* use this for casts */
-typedef xfs_bmbt_rec_t xfs_bmdr_rec_t;
-
-typedef struct xfs_bmbt_rec_host {
-       __uint64_t              l0, l1;
-} xfs_bmbt_rec_host_t;
-
-/*
- * Values and macros for delayed-allocation startblock fields.
- */
-#define STARTBLOCKVALBITS      17
-#define STARTBLOCKMASKBITS     (15 + XFS_BIG_BLKNOS * 20)
-#define DSTARTBLOCKMASKBITS    (15 + 20)
-#define STARTBLOCKMASK         \
-       (((((xfs_fsblock_t)1) << STARTBLOCKMASKBITS) - 1) << STARTBLOCKVALBITS)
-#define DSTARTBLOCKMASK                \
-       (((((xfs_dfsbno_t)1) << DSTARTBLOCKMASKBITS) - 1) << STARTBLOCKVALBITS)
-
-static inline int isnullstartblock(xfs_fsblock_t x)
-{
-       return ((x) & STARTBLOCKMASK) == STARTBLOCKMASK;
-}
-
-static inline int isnulldstartblock(xfs_dfsbno_t x)
-{
-       return ((x) & DSTARTBLOCKMASK) == DSTARTBLOCKMASK;
-}
-
-static inline xfs_fsblock_t nullstartblock(int k)
-{
-       ASSERT(k < (1 << STARTBLOCKVALBITS));
-       return STARTBLOCKMASK | (k);
-}
-
-static inline xfs_filblks_t startblockval(xfs_fsblock_t x)
-{
-       return (xfs_filblks_t)((x) & ~STARTBLOCKMASK);
-}
-
-/*
- * Possible extent formats.
- */
-typedef enum {
-       XFS_EXTFMT_NOSTATE = 0,
-       XFS_EXTFMT_HASSTATE
-} xfs_exntfmt_t;
-
-/*
- * Possible extent states.
- */
-typedef enum {
-       XFS_EXT_NORM, XFS_EXT_UNWRITTEN,
-       XFS_EXT_DMAPI_OFFLINE, XFS_EXT_INVALID
-} xfs_exntst_t;
-
 /*
  * Extent state and extent format macros.
  */
@@ -114,27 +32,6 @@ typedef enum {
                XFS_EXTFMT_HASSTATE : XFS_EXTFMT_NOSTATE)
 #define ISUNWRITTEN(x) ((x)->br_state == XFS_EXT_UNWRITTEN)
 
-/*
- * Incore version of above.
- */
-typedef struct xfs_bmbt_irec
-{
-       xfs_fileoff_t   br_startoff;    /* starting file offset */
-       xfs_fsblock_t   br_startblock;  /* starting block number */
-       xfs_filblks_t   br_blockcount;  /* number of blocks */
-       xfs_exntst_t    br_state;       /* extent state */
-} xfs_bmbt_irec_t;
-
-/*
- * Key structure for non-leaf levels of the tree.
- */
-typedef struct xfs_bmbt_key {
-       __be64          br_startoff;    /* starting file offset */
-} xfs_bmbt_key_t, xfs_bmdr_key_t;
-
-/* btree pointer type */
-typedef __be64 xfs_bmbt_ptr_t, xfs_bmdr_ptr_t;
-
 /*
  * Btree block header size depends on a superblock flag.
  */
@@ -243,6 +140,4 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
 extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
                struct xfs_trans *, struct xfs_inode *, int);
 
-extern const struct xfs_buf_ops xfs_bmbt_buf_ops;
-
 #endif /* __XFS_BMAP_BTREE_H__ */
index 97f952caea74bd8311a3269ca7520e87b01fcb18..5887e41c0323ae85f867cc9bc83bbdf8c1e41cd1 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
+#include "xfs_trans.h"
 #include "xfs_extfree_item.h"
 #include "xfs_alloc.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_rtalloc.h"
 #include "xfs_error.h"
 #include "xfs_quota.h"
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
+#include "xfs_log.h"
+#include "xfs_dinode.h"
 
 /* Kernel only BMAP related definitions and functions */
 
@@ -965,32 +965,12 @@ xfs_free_eofblocks(
        return error;
 }
 
-/*
- * xfs_alloc_file_space()
- *      This routine allocates disk space for the given file.
- *
- *     If alloc_type == 0, this request is for an ALLOCSP type
- *     request which will change the file size.  In this case, no
- *     DMAPI event will be generated by the call.  A TRUNCATE event
- *     will be generated later by xfs_setattr.
- *
- *     If alloc_type != 0, this request is for a RESVSP type
- *     request, and a DMAPI DM_EVENT_WRITE will be generated if the
- *     lower block boundary byte address is less than the file's
- *     length.
- *
- * RETURNS:
- *       0 on success
- *      errno on error
- *
- */
-STATIC int
+int
 xfs_alloc_file_space(
-       xfs_inode_t             *ip,
+       struct xfs_inode        *ip,
        xfs_off_t               offset,
        xfs_off_t               len,
-       int                     alloc_type,
-       int                     attr_flags)
+       int                     alloc_type)
 {
        xfs_mount_t             *mp = ip->i_mount;
        xfs_off_t               count;
@@ -1232,24 +1212,11 @@ xfs_zero_remaining_bytes(
        return error;
 }
 
-/*
- * xfs_free_file_space()
- *      This routine frees disk space for the given file.
- *
- *     This routine is only called by xfs_change_file_space
- *     for an UNRESVSP type call.
- *
- * RETURNS:
- *       0 on success
- *      errno on error
- *
- */
-STATIC int
+int
 xfs_free_file_space(
-       xfs_inode_t             *ip,
+       struct xfs_inode        *ip,
        xfs_off_t               offset,
-       xfs_off_t               len,
-       int                     attr_flags)
+       xfs_off_t               len)
 {
        int                     committed;
        int                     done;
@@ -1267,7 +1234,6 @@ xfs_free_file_space(
        int                     rt;
        xfs_fileoff_t           startoffset_fsb;
        xfs_trans_t             *tp;
-       int                     need_iolock = 1;
 
        mp = ip->i_mount;
 
@@ -1284,20 +1250,15 @@ xfs_free_file_space(
        startoffset_fsb = XFS_B_TO_FSB(mp, offset);
        endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len);
 
-       if (attr_flags & XFS_ATTR_NOLOCK)
-               need_iolock = 0;
-       if (need_iolock) {
-               xfs_ilock(ip, XFS_IOLOCK_EXCL);
-               /* wait for the completion of any pending DIOs */
-               inode_dio_wait(VFS_I(ip));
-       }
+       /* wait for the completion of any pending DIOs */
+       inode_dio_wait(VFS_I(ip));
 
        rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE);
        ioffset = offset & ~(rounding - 1);
        error = -filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
                                              ioffset, -1);
        if (error)
-               goto out_unlock_iolock;
+               goto out;
        truncate_pagecache_range(VFS_I(ip), ioffset, -1);
 
        /*
@@ -1311,7 +1272,7 @@ xfs_free_file_space(
                error = xfs_bmapi_read(ip, startoffset_fsb, 1,
                                        &imap, &nimap, 0);
                if (error)
-                       goto out_unlock_iolock;
+                       goto out;
                ASSERT(nimap == 0 || nimap == 1);
                if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
                        xfs_daddr_t     block;
@@ -1326,7 +1287,7 @@ xfs_free_file_space(
                error = xfs_bmapi_read(ip, endoffset_fsb - 1, 1,
                                        &imap, &nimap, 0);
                if (error)
-                       goto out_unlock_iolock;
+                       goto out;
                ASSERT(nimap == 0 || nimap == 1);
                if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
                        ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
@@ -1412,27 +1373,23 @@ xfs_free_file_space(
                xfs_iunlock(ip, XFS_ILOCK_EXCL);
        }
 
- out_unlock_iolock:
-       if (need_iolock)
-               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ out:
        return error;
 
  error0:
        xfs_bmap_cancel(&free_list);
  error1:
        xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-       xfs_iunlock(ip, need_iolock ? (XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL) :
-                   XFS_ILOCK_EXCL);
-       return error;
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       goto out;
 }
 
 
-STATIC int
+int
 xfs_zero_file_space(
        struct xfs_inode        *ip,
        xfs_off_t               offset,
-       xfs_off_t               len,
-       int                     attr_flags)
+       xfs_off_t               len)
 {
        struct xfs_mount        *mp = ip->i_mount;
        uint                    granularity;
@@ -1453,9 +1410,6 @@ xfs_zero_file_space(
        ASSERT(start_boundary >= offset);
        ASSERT(end_boundary <= offset + len);
 
-       if (!(attr_flags & XFS_ATTR_NOLOCK))
-               xfs_ilock(ip, XFS_IOLOCK_EXCL);
-
        if (start_boundary < end_boundary - 1) {
                /* punch out the page cache over the conversion range */
                truncate_pagecache_range(VFS_I(ip), start_boundary,
@@ -1463,16 +1417,16 @@ xfs_zero_file_space(
                /* convert the blocks */
                error = xfs_alloc_file_space(ip, start_boundary,
                                        end_boundary - start_boundary - 1,
-                                       XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT,
-                                       attr_flags);
+                                       XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT);
                if (error)
-                       goto out_unlock;
+                       goto out;
 
                /* We've handled the interior of the range, now for the edges */
-               if (start_boundary != offset)
+               if (start_boundary != offset) {
                        error = xfs_iozero(ip, offset, start_boundary - offset);
-               if (error)
-                       goto out_unlock;
+                       if (error)
+                               goto out;
+               }
 
                if (end_boundary != offset + len)
                        error = xfs_iozero(ip, end_boundary,
@@ -1486,196 +1440,11 @@ xfs_zero_file_space(
                error = xfs_iozero(ip, offset, len);
        }
 
-out_unlock:
-       if (!(attr_flags & XFS_ATTR_NOLOCK))
-               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+out:
        return error;
 
 }
 
-/*
- * xfs_change_file_space()
- *      This routine allocates or frees disk space for the given file.
- *      The user specified parameters are checked for alignment and size
- *      limitations.
- *
- * RETURNS:
- *       0 on success
- *      errno on error
- *
- */
-int
-xfs_change_file_space(
-       xfs_inode_t     *ip,
-       int             cmd,
-       xfs_flock64_t   *bf,
-       xfs_off_t       offset,
-       int             attr_flags)
-{
-       xfs_mount_t     *mp = ip->i_mount;
-       int             clrprealloc;
-       int             error;
-       xfs_fsize_t     fsize;
-       int             setprealloc;
-       xfs_off_t       startoffset;
-       xfs_trans_t     *tp;
-       struct iattr    iattr;
-
-       if (!S_ISREG(ip->i_d.di_mode))
-               return XFS_ERROR(EINVAL);
-
-       switch (bf->l_whence) {
-       case 0: /*SEEK_SET*/
-               break;
-       case 1: /*SEEK_CUR*/
-               bf->l_start += offset;
-               break;
-       case 2: /*SEEK_END*/
-               bf->l_start += XFS_ISIZE(ip);
-               break;
-       default:
-               return XFS_ERROR(EINVAL);
-       }
-
-       /*
-        * length of <= 0 for resv/unresv/zero is invalid.  length for
-        * alloc/free is ignored completely and we have no idea what userspace
-        * might have set it to, so set it to zero to allow range
-        * checks to pass.
-        */
-       switch (cmd) {
-       case XFS_IOC_ZERO_RANGE:
-       case XFS_IOC_RESVSP:
-       case XFS_IOC_RESVSP64:
-       case XFS_IOC_UNRESVSP:
-       case XFS_IOC_UNRESVSP64:
-               if (bf->l_len <= 0)
-                       return XFS_ERROR(EINVAL);
-               break;
-       default:
-               bf->l_len = 0;
-               break;
-       }
-
-       if (bf->l_start < 0 ||
-           bf->l_start > mp->m_super->s_maxbytes ||
-           bf->l_start + bf->l_len < 0 ||
-           bf->l_start + bf->l_len >= mp->m_super->s_maxbytes)
-               return XFS_ERROR(EINVAL);
-
-       bf->l_whence = 0;
-
-       startoffset = bf->l_start;
-       fsize = XFS_ISIZE(ip);
-
-       setprealloc = clrprealloc = 0;
-       switch (cmd) {
-       case XFS_IOC_ZERO_RANGE:
-               error = xfs_zero_file_space(ip, startoffset, bf->l_len,
-                                               attr_flags);
-               if (error)
-                       return error;
-               setprealloc = 1;
-               break;
-
-       case XFS_IOC_RESVSP:
-       case XFS_IOC_RESVSP64:
-               error = xfs_alloc_file_space(ip, startoffset, bf->l_len,
-                                               XFS_BMAPI_PREALLOC, attr_flags);
-               if (error)
-                       return error;
-               setprealloc = 1;
-               break;
-
-       case XFS_IOC_UNRESVSP:
-       case XFS_IOC_UNRESVSP64:
-               if ((error = xfs_free_file_space(ip, startoffset, bf->l_len,
-                                                               attr_flags)))
-                       return error;
-               break;
-
-       case XFS_IOC_ALLOCSP:
-       case XFS_IOC_ALLOCSP64:
-       case XFS_IOC_FREESP:
-       case XFS_IOC_FREESP64:
-               /*
-                * These operations actually do IO when extending the file, but
-                * the allocation is done seperately to the zeroing that is
-                * done. This set of operations need to be serialised against
-                * other IO operations, such as truncate and buffered IO. We
-                * need to take the IOLOCK here to serialise the allocation and
-                * zeroing IO to prevent other IOLOCK holders (e.g. getbmap,
-                * truncate, direct IO) from racing against the transient
-                * allocated but not written state we can have here.
-                */
-               xfs_ilock(ip, XFS_IOLOCK_EXCL);
-               if (startoffset > fsize) {
-                       error = xfs_alloc_file_space(ip, fsize,
-                                       startoffset - fsize, 0,
-                                       attr_flags | XFS_ATTR_NOLOCK);
-                       if (error) {
-                               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-                               break;
-                       }
-               }
-
-               iattr.ia_valid = ATTR_SIZE;
-               iattr.ia_size = startoffset;
-
-               error = xfs_setattr_size(ip, &iattr,
-                                        attr_flags | XFS_ATTR_NOLOCK);
-               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-
-               if (error)
-                       return error;
-
-               clrprealloc = 1;
-               break;
-
-       default:
-               ASSERT(0);
-               return XFS_ERROR(EINVAL);
-       }
-
-       /*
-        * update the inode timestamp, mode, and prealloc flag bits
-        */
-       tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID);
-       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0);
-       if (error) {
-               xfs_trans_cancel(tp, 0);
-               return error;
-       }
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
-
-       if ((attr_flags & XFS_ATTR_DMI) == 0) {
-               ip->i_d.di_mode &= ~S_ISUID;
-
-               /*
-                * Note that we don't have to worry about mandatory
-                * file locking being disabled here because we only
-                * clear the S_ISGID bit if the Group execute bit is
-                * on, but if it was on then mandatory locking wouldn't
-                * have been enabled.
-                */
-               if (ip->i_d.di_mode & S_IXGRP)
-                       ip->i_d.di_mode &= ~S_ISGID;
-
-               xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
-       }
-       if (setprealloc)
-               ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
-       else if (clrprealloc)
-               ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
-
-       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       if (attr_flags & XFS_ATTR_SYNC)
-               xfs_trans_set_sync(tp);
-       return xfs_trans_commit(tp, 0);
-}
-
 /*
  * We need to check that the format of the data fork in the temporary inode is
  * valid for the target inode before doing the swap. This is not a problem with
index 061260946f7a85ae7d972ea11f0142daca9ba482..900747b25772c2b1a41821fba8e99c3cde2b3ffc 100644 (file)
@@ -93,9 +93,12 @@ int  xfs_bmap_last_extent(struct xfs_trans *tp, struct xfs_inode *ip,
                             int *is_empty);
 
 /* preallocation and hole punch interface */
-int    xfs_change_file_space(struct xfs_inode *ip, int cmd,
-                             xfs_flock64_t *bf, xfs_off_t offset,
-                             int attr_flags);
+int    xfs_alloc_file_space(struct xfs_inode *ip, xfs_off_t offset,
+                            xfs_off_t len, int alloc_type);
+int    xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset,
+                           xfs_off_t len);
+int    xfs_zero_file_space(struct xfs_inode *ip, xfs_off_t offset,
+                           xfs_off_t len);
 
 /* EOF block manipulation functions */
 bool   xfs_can_free_eofblocks(struct xfs_inode *ip, bool force);
index 5690e102243d70e7b87876f0ef9bc07be058da86..9adaae4f3e2fd21c647c9e7fa023a120e5012272 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_buf_item.h"
 #include "xfs_btree.h"
index 06729b67ad58ec2c394a3ecdb1104938ced672c2..91e34f21bacea773eaadecfc0a3c29595e912601 100644 (file)
@@ -26,73 +26,6 @@ struct xfs_trans;
 
 extern kmem_zone_t     *xfs_btree_cur_zone;
 
-/*
- * This nonsense is to make -wlint happy.
- */
-#define        XFS_LOOKUP_EQ   ((xfs_lookup_t)XFS_LOOKUP_EQi)
-#define        XFS_LOOKUP_LE   ((xfs_lookup_t)XFS_LOOKUP_LEi)
-#define        XFS_LOOKUP_GE   ((xfs_lookup_t)XFS_LOOKUP_GEi)
-
-#define        XFS_BTNUM_BNO   ((xfs_btnum_t)XFS_BTNUM_BNOi)
-#define        XFS_BTNUM_CNT   ((xfs_btnum_t)XFS_BTNUM_CNTi)
-#define        XFS_BTNUM_BMAP  ((xfs_btnum_t)XFS_BTNUM_BMAPi)
-#define        XFS_BTNUM_INO   ((xfs_btnum_t)XFS_BTNUM_INOi)
-
-/*
- * Generic btree header.
- *
- * This is a combination of the actual format used on disk for short and long
- * format btrees.  The first three fields are shared by both format, but the
- * pointers are different and should be used with care.
- *
- * To get the size of the actual short or long form headers please use the size
- * macros below.  Never use sizeof(xfs_btree_block).
- *
- * The blkno, crc, lsn, owner and uuid fields are only available in filesystems
- * with the crc feature bit, and all accesses to them must be conditional on
- * that flag.
- */
-struct xfs_btree_block {
-       __be32          bb_magic;       /* magic number for block type */
-       __be16          bb_level;       /* 0 is a leaf */
-       __be16          bb_numrecs;     /* current # of data records */
-       union {
-               struct {
-                       __be32          bb_leftsib;
-                       __be32          bb_rightsib;
-
-                       __be64          bb_blkno;
-                       __be64          bb_lsn;
-                       uuid_t          bb_uuid;
-                       __be32          bb_owner;
-                       __le32          bb_crc;
-               } s;                    /* short form pointers */
-               struct  {
-                       __be64          bb_leftsib;
-                       __be64          bb_rightsib;
-
-                       __be64          bb_blkno;
-                       __be64          bb_lsn;
-                       uuid_t          bb_uuid;
-                       __be64          bb_owner;
-                       __le32          bb_crc;
-                       __be32          bb_pad; /* padding for alignment */
-               } l;                    /* long form pointers */
-       } bb_u;                         /* rest */
-};
-
-#define XFS_BTREE_SBLOCK_LEN   16      /* size of a short form block */
-#define XFS_BTREE_LBLOCK_LEN   24      /* size of a long form block */
-
-/* sizes of CRC enabled btree blocks */
-#define XFS_BTREE_SBLOCK_CRC_LEN       (XFS_BTREE_SBLOCK_LEN + 40)
-#define XFS_BTREE_LBLOCK_CRC_LEN       (XFS_BTREE_LBLOCK_LEN + 48)
-
-#define XFS_BTREE_SBLOCK_CRC_OFF \
-       offsetof(struct xfs_btree_block, bb_u.s.bb_crc)
-#define XFS_BTREE_LBLOCK_CRC_OFF \
-       offsetof(struct xfs_btree_block, bb_u.l.bb_crc)
-
 /*
  * Generic key, ptr and record wrapper structures.
  *
@@ -118,6 +51,18 @@ union xfs_btree_rec {
        xfs_inobt_rec_t         inobt;
 };
 
+/*
+ * This nonsense is to make -wlint happy.
+ */
+#define        XFS_LOOKUP_EQ   ((xfs_lookup_t)XFS_LOOKUP_EQi)
+#define        XFS_LOOKUP_LE   ((xfs_lookup_t)XFS_LOOKUP_LEi)
+#define        XFS_LOOKUP_GE   ((xfs_lookup_t)XFS_LOOKUP_GEi)
+
+#define        XFS_BTNUM_BNO   ((xfs_btnum_t)XFS_BTNUM_BNOi)
+#define        XFS_BTNUM_CNT   ((xfs_btnum_t)XFS_BTNUM_CNTi)
+#define        XFS_BTNUM_BMAP  ((xfs_btnum_t)XFS_BTNUM_BMAPi)
+#define        XFS_BTNUM_INO   ((xfs_btnum_t)XFS_BTNUM_INOi)
+
 /*
  * For logging record fields.
  */
index 263470075ea2ce11dd39a83603e0b9fa1f0f5a60..c7f0b77dcb0090046b84eda27c68d870af25d45a 100644 (file)
 #include <linux/backing-dev.h>
 #include <linux/freezer.h>
 
-#include "xfs_sb.h"
+#include "xfs_log_format.h"
 #include "xfs_trans_resv.h"
-#include "xfs_log.h"
+#include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
 #include "xfs_trace.h"
+#include "xfs_log.h"
 
 static kmem_zone_t *xfs_buf_zone;
 
@@ -590,7 +591,7 @@ found:
                error = _xfs_buf_map_pages(bp, flags);
                if (unlikely(error)) {
                        xfs_warn(target->bt_mount,
-                               "%s: failed to map pages\n", __func__);
+                               "%s: failed to map pagesn", __func__);
                        xfs_buf_relse(bp);
                        return NULL;
                }
@@ -809,7 +810,7 @@ xfs_buf_get_uncached(
        error = _xfs_buf_map_pages(bp, 0);
        if (unlikely(error)) {
                xfs_warn(target->bt_mount,
-                       "%s: failed to map pages\n", __func__);
+                       "%s: failed to map pages", __func__);
                goto fail_free_mem;
        }
 
@@ -1618,7 +1619,7 @@ xfs_setsize_buftarg_flags(
                bdevname(btp->bt_bdev, name);
 
                xfs_warn(btp->bt_mount,
-                       "Cannot set_blocksize to %u on device %s\n",
+                       "Cannot set_blocksize to %u on device %s",
                        sectorsize, name);
                return EINVAL;
        }
index f1d85cfc0a54d1cb54ebe95937872127f2e63142..a64f67ba25d3c99c748e1d4a8f84f7648e3dd651 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_trans_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_log.h"
 
 
 kmem_zone_t    *xfs_buf_item_zone;
@@ -808,7 +809,7 @@ xfs_buf_item_init(
  * Mark bytes first through last inclusive as dirty in the buf
  * item's bitmap.
  */
-void
+static void
 xfs_buf_item_log_segment(
        struct xfs_buf_log_item *bip,
        uint                    first,
index db6371087fe8ea9b786f82189b387c55979a2460..3f3455a415102de167271a7467725da410a21f24 100644 (file)
@@ -71,10 +71,6 @@ void xfs_buf_attach_iodone(struct xfs_buf *,
 void   xfs_buf_iodone_callbacks(struct xfs_buf *);
 void   xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *);
 
-void   xfs_trans_buf_set_type(struct xfs_trans *, struct xfs_buf *,
-                              enum xfs_blft);
-void   xfs_trans_buf_copy_type(struct xfs_buf *dst_bp, struct xfs_buf *src_bp);
-
 extern kmem_zone_t     *xfs_buf_item_zone;
 
 #endif /* __XFS_BUF_ITEM_H__ */
index 20bf8e8002d6fd2733782af97f373ddaf2bcd8d6..796272a2e1298fca3a5cc08ef4c6b226d6eec15c 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_alloc.h"
 #include "xfs_bmap.h"
@@ -129,56 +129,6 @@ xfs_da_state_free(xfs_da_state_t *state)
        kmem_zone_free(xfs_da_state_zone, state);
 }
 
-void
-xfs_da3_node_hdr_from_disk(
-       struct xfs_da3_icnode_hdr       *to,
-       struct xfs_da_intnode           *from)
-{
-       ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC) ||
-              from->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC));
-
-       if (from->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC)) {
-               struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)from;
-
-               to->forw = be32_to_cpu(hdr3->info.hdr.forw);
-               to->back = be32_to_cpu(hdr3->info.hdr.back);
-               to->magic = be16_to_cpu(hdr3->info.hdr.magic);
-               to->count = be16_to_cpu(hdr3->__count);
-               to->level = be16_to_cpu(hdr3->__level);
-               return;
-       }
-       to->forw = be32_to_cpu(from->hdr.info.forw);
-       to->back = be32_to_cpu(from->hdr.info.back);
-       to->magic = be16_to_cpu(from->hdr.info.magic);
-       to->count = be16_to_cpu(from->hdr.__count);
-       to->level = be16_to_cpu(from->hdr.__level);
-}
-
-void
-xfs_da3_node_hdr_to_disk(
-       struct xfs_da_intnode           *to,
-       struct xfs_da3_icnode_hdr       *from)
-{
-       ASSERT(from->magic == XFS_DA_NODE_MAGIC ||
-              from->magic == XFS_DA3_NODE_MAGIC);
-
-       if (from->magic == XFS_DA3_NODE_MAGIC) {
-               struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)to;
-
-               hdr3->info.hdr.forw = cpu_to_be32(from->forw);
-               hdr3->info.hdr.back = cpu_to_be32(from->back);
-               hdr3->info.hdr.magic = cpu_to_be16(from->magic);
-               hdr3->__count = cpu_to_be16(from->count);
-               hdr3->__level = cpu_to_be16(from->level);
-               return;
-       }
-       to->hdr.info.forw = cpu_to_be32(from->forw);
-       to->hdr.info.back = cpu_to_be32(from->back);
-       to->hdr.info.magic = cpu_to_be16(from->magic);
-       to->hdr.__count = cpu_to_be16(from->count);
-       to->hdr.__level = cpu_to_be16(from->level);
-}
-
 static bool
 xfs_da3_node_verify(
        struct xfs_buf          *bp)
@@ -186,8 +136,11 @@ xfs_da3_node_verify(
        struct xfs_mount        *mp = bp->b_target->bt_mount;
        struct xfs_da_intnode   *hdr = bp->b_addr;
        struct xfs_da3_icnode_hdr ichdr;
+       const struct xfs_dir_ops *ops;
 
-       xfs_da3_node_hdr_from_disk(&ichdr, hdr);
+       ops = xfs_dir_get_ops(mp, NULL);
+
+       ops->node_hdr_from_disk(&ichdr, hdr);
 
        if (xfs_sb_version_hascrc(&mp->m_sb)) {
                struct xfs_da3_node_hdr *hdr3 = bp->b_addr;
@@ -354,11 +307,12 @@ xfs_da3_node_create(
        struct xfs_da3_icnode_hdr ichdr = {0};
        struct xfs_buf          *bp;
        int                     error;
+       struct xfs_inode        *dp = args->dp;
 
        trace_xfs_da_node_create(args);
        ASSERT(level <= XFS_DA_NODE_MAXDEPTH);
 
-       error = xfs_da_get_buf(tp, args->dp, blkno, -1, &bp, whichfork);
+       error = xfs_da_get_buf(tp, dp, blkno, -1, &bp, whichfork);
        if (error)
                return(error);
        bp->b_ops = &xfs_da3_node_buf_ops;
@@ -377,9 +331,9 @@ xfs_da3_node_create(
        }
        ichdr.level = level;
 
-       xfs_da3_node_hdr_to_disk(node, &ichdr);
+       dp->d_ops->node_hdr_to_disk(node, &ichdr);
        xfs_trans_log_buf(tp, bp,
-               XFS_DA_LOGRANGE(node, &node->hdr, xfs_da3_node_hdr_size(node)));
+               XFS_DA_LOGRANGE(node, &node->hdr, dp->d_ops->node_hdr_size));
 
        *bpp = bp;
        return(0);
@@ -589,8 +543,8 @@ xfs_da3_root_split(
            oldroot->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC)) {
                struct xfs_da3_icnode_hdr nodehdr;
 
-               xfs_da3_node_hdr_from_disk(&nodehdr, oldroot);
-               btree = xfs_da3_node_tree_p(oldroot);
+               dp->d_ops->node_hdr_from_disk(&nodehdr, oldroot);
+               btree = dp->d_ops->node_tree_p(oldroot);
                size = (int)((char *)&btree[nodehdr.count] - (char *)oldroot);
                level = nodehdr.level;
 
@@ -604,8 +558,8 @@ xfs_da3_root_split(
                struct xfs_dir2_leaf_entry *ents;
 
                leaf = (xfs_dir2_leaf_t *)oldroot;
-               xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-               ents = xfs_dir3_leaf_ents_p(leaf);
+               dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+               ents = dp->d_ops->leaf_ents_p(leaf);
 
                ASSERT(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
                       leafhdr.magic == XFS_DIR3_LEAFN_MAGIC);
@@ -649,14 +603,14 @@ xfs_da3_root_split(
                return error;
 
        node = bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
-       btree = xfs_da3_node_tree_p(node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+       btree = dp->d_ops->node_tree_p(node);
        btree[0].hashval = cpu_to_be32(blk1->hashval);
        btree[0].before = cpu_to_be32(blk1->blkno);
        btree[1].hashval = cpu_to_be32(blk2->hashval);
        btree[1].before = cpu_to_be32(blk2->blkno);
        nodehdr.count = 2;
-       xfs_da3_node_hdr_to_disk(node, &nodehdr);
+       dp->d_ops->node_hdr_to_disk(node, &nodehdr);
 
 #ifdef DEBUG
        if (oldroot->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) ||
@@ -693,11 +647,12 @@ xfs_da3_node_split(
        int                     newcount;
        int                     error;
        int                     useextra;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_node_split(state->args);
 
        node = oldblk->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
 
        /*
         * With V2 dirs the extra block is data or freespace.
@@ -744,7 +699,7 @@ xfs_da3_node_split(
         * If we had double-split op below us, then add the extra block too.
         */
        node = oldblk->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
        if (oldblk->index <= nodehdr.count) {
                oldblk->index++;
                xfs_da3_node_add(state, oldblk, addblk);
@@ -793,15 +748,16 @@ xfs_da3_node_rebalance(
        int                     count;
        int                     tmp;
        int                     swap = 0;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_node_rebalance(state->args);
 
        node1 = blk1->bp->b_addr;
        node2 = blk2->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr1, node1);
-       xfs_da3_node_hdr_from_disk(&nodehdr2, node2);
-       btree1 = xfs_da3_node_tree_p(node1);
-       btree2 = xfs_da3_node_tree_p(node2);
+       dp->d_ops->node_hdr_from_disk(&nodehdr1, node1);
+       dp->d_ops->node_hdr_from_disk(&nodehdr2, node2);
+       btree1 = dp->d_ops->node_tree_p(node1);
+       btree2 = dp->d_ops->node_tree_p(node2);
 
        /*
         * Figure out how many entries need to move, and in which direction.
@@ -814,10 +770,10 @@ xfs_da3_node_rebalance(
                tmpnode = node1;
                node1 = node2;
                node2 = tmpnode;
-               xfs_da3_node_hdr_from_disk(&nodehdr1, node1);
-               xfs_da3_node_hdr_from_disk(&nodehdr2, node2);
-               btree1 = xfs_da3_node_tree_p(node1);
-               btree2 = xfs_da3_node_tree_p(node2);
+               dp->d_ops->node_hdr_from_disk(&nodehdr1, node1);
+               dp->d_ops->node_hdr_from_disk(&nodehdr2, node2);
+               btree1 = dp->d_ops->node_tree_p(node1);
+               btree2 = dp->d_ops->node_tree_p(node2);
                swap = 1;
        }
 
@@ -879,15 +835,14 @@ xfs_da3_node_rebalance(
        /*
         * Log header of node 1 and all current bits of node 2.
         */
-       xfs_da3_node_hdr_to_disk(node1, &nodehdr1);
+       dp->d_ops->node_hdr_to_disk(node1, &nodehdr1);
        xfs_trans_log_buf(tp, blk1->bp,
-               XFS_DA_LOGRANGE(node1, &node1->hdr,
-                               xfs_da3_node_hdr_size(node1)));
+               XFS_DA_LOGRANGE(node1, &node1->hdr, dp->d_ops->node_hdr_size));
 
-       xfs_da3_node_hdr_to_disk(node2, &nodehdr2);
+       dp->d_ops->node_hdr_to_disk(node2, &nodehdr2);
        xfs_trans_log_buf(tp, blk2->bp,
                XFS_DA_LOGRANGE(node2, &node2->hdr,
-                               xfs_da3_node_hdr_size(node2) +
+                               dp->d_ops->node_hdr_size +
                                (sizeof(btree2[0]) * nodehdr2.count)));
 
        /*
@@ -897,10 +852,10 @@ xfs_da3_node_rebalance(
        if (swap) {
                node1 = blk1->bp->b_addr;
                node2 = blk2->bp->b_addr;
-               xfs_da3_node_hdr_from_disk(&nodehdr1, node1);
-               xfs_da3_node_hdr_from_disk(&nodehdr2, node2);
-               btree1 = xfs_da3_node_tree_p(node1);
-               btree2 = xfs_da3_node_tree_p(node2);
+               dp->d_ops->node_hdr_from_disk(&nodehdr1, node1);
+               dp->d_ops->node_hdr_from_disk(&nodehdr2, node2);
+               btree1 = dp->d_ops->node_tree_p(node1);
+               btree2 = dp->d_ops->node_tree_p(node2);
        }
        blk1->hashval = be32_to_cpu(btree1[nodehdr1.count - 1].hashval);
        blk2->hashval = be32_to_cpu(btree2[nodehdr2.count - 1].hashval);
@@ -927,12 +882,13 @@ xfs_da3_node_add(
        struct xfs_da3_icnode_hdr nodehdr;
        struct xfs_da_node_entry *btree;
        int                     tmp;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_node_add(state->args);
 
        node = oldblk->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
-       btree = xfs_da3_node_tree_p(node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+       btree = dp->d_ops->node_tree_p(node);
 
        ASSERT(oldblk->index >= 0 && oldblk->index <= nodehdr.count);
        ASSERT(newblk->blkno != 0);
@@ -955,9 +911,9 @@ xfs_da3_node_add(
                                tmp + sizeof(*btree)));
 
        nodehdr.count += 1;
-       xfs_da3_node_hdr_to_disk(node, &nodehdr);
+       dp->d_ops->node_hdr_to_disk(node, &nodehdr);
        xfs_trans_log_buf(state->args->trans, oldblk->bp,
-               XFS_DA_LOGRANGE(node, &node->hdr, xfs_da3_node_hdr_size(node)));
+               XFS_DA_LOGRANGE(node, &node->hdr, dp->d_ops->node_hdr_size));
 
        /*
         * Copy the last hash value from the oldblk to propagate upwards.
@@ -1094,6 +1050,7 @@ xfs_da3_root_join(
        struct xfs_da3_icnode_hdr oldroothdr;
        struct xfs_da_node_entry *btree;
        int                     error;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_root_join(state->args);
 
@@ -1101,7 +1058,7 @@ xfs_da3_root_join(
 
        args = state->args;
        oldroot = root_blk->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&oldroothdr, oldroot);
+       dp->d_ops->node_hdr_from_disk(&oldroothdr, oldroot);
        ASSERT(oldroothdr.forw == 0);
        ASSERT(oldroothdr.back == 0);
 
@@ -1115,10 +1072,10 @@ xfs_da3_root_join(
         * Read in the (only) child block, then copy those bytes into
         * the root block's buffer and free the original child block.
         */
-       btree = xfs_da3_node_tree_p(oldroot);
+       btree = dp->d_ops->node_tree_p(oldroot);
        child = be32_to_cpu(btree[0].before);
        ASSERT(child != 0);
-       error = xfs_da3_node_read(args->trans, args->dp, child, -1, &bp,
+       error = xfs_da3_node_read(args->trans, dp, child, -1, &bp,
                                             args->whichfork);
        if (error)
                return error;
@@ -1168,6 +1125,7 @@ xfs_da3_node_toosmall(
        int                     error;
        int                     retval;
        int                     i;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_node_toosmall(state->args);
 
@@ -1179,7 +1137,7 @@ xfs_da3_node_toosmall(
        blk = &state->path.blk[ state->path.active-1 ];
        info = blk->bp->b_addr;
        node = (xfs_da_intnode_t *)info;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
        if (nodehdr.count > (state->node_ents >> 1)) {
                *action = 0;    /* blk over 50%, don't try to join */
                return(0);      /* blk over 50%, don't try to join */
@@ -1231,13 +1189,13 @@ xfs_da3_node_toosmall(
                        blkno = nodehdr.back;
                if (blkno == 0)
                        continue;
-               error = xfs_da3_node_read(state->args->trans, state->args->dp,
+               error = xfs_da3_node_read(state->args->trans, dp,
                                        blkno, -1, &bp, state->args->whichfork);
                if (error)
                        return(error);
 
                node = bp->b_addr;
-               xfs_da3_node_hdr_from_disk(&thdr, node);
+               dp->d_ops->node_hdr_from_disk(&thdr, node);
                xfs_trans_brelse(state->args->trans, bp);
 
                if (count - thdr.count >= 0)
@@ -1275,6 +1233,7 @@ xfs_da3_node_toosmall(
  */
 STATIC uint
 xfs_da3_node_lasthash(
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp,
        int                     *count)
 {
@@ -1283,12 +1242,12 @@ xfs_da3_node_lasthash(
        struct xfs_da3_icnode_hdr nodehdr;
 
        node = bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
        if (count)
                *count = nodehdr.count;
        if (!nodehdr.count)
                return 0;
-       btree = xfs_da3_node_tree_p(node);
+       btree = dp->d_ops->node_tree_p(node);
        return be32_to_cpu(btree[nodehdr.count - 1].hashval);
 }
 
@@ -1307,6 +1266,7 @@ xfs_da3_fixhashpath(
        xfs_dahash_t            lasthash=0;
        int                     level;
        int                     count;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_fixhashpath(state->args);
 
@@ -1319,12 +1279,12 @@ xfs_da3_fixhashpath(
                        return;
                break;
        case XFS_DIR2_LEAFN_MAGIC:
-               lasthash = xfs_dir2_leafn_lasthash(blk->bp, &count);
+               lasthash = xfs_dir2_leafn_lasthash(dp, blk->bp, &count);
                if (count == 0)
                        return;
                break;
        case XFS_DA_NODE_MAGIC:
-               lasthash = xfs_da3_node_lasthash(blk->bp, &count);
+               lasthash = xfs_da3_node_lasthash(dp, blk->bp, &count);
                if (count == 0)
                        return;
                break;
@@ -1333,8 +1293,8 @@ xfs_da3_fixhashpath(
                struct xfs_da3_icnode_hdr nodehdr;
 
                node = blk->bp->b_addr;
-               xfs_da3_node_hdr_from_disk(&nodehdr, node);
-               btree = xfs_da3_node_tree_p(node);
+               dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+               btree = dp->d_ops->node_tree_p(node);
                if (be32_to_cpu(btree->hashval) == lasthash)
                        break;
                blk->hashval = lasthash;
@@ -1360,11 +1320,12 @@ xfs_da3_node_remove(
        struct xfs_da_node_entry *btree;
        int                     index;
        int                     tmp;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_node_remove(state->args);
 
        node = drop_blk->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&nodehdr, node);
+       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
        ASSERT(drop_blk->index < nodehdr.count);
        ASSERT(drop_blk->index >= 0);
 
@@ -1372,7 +1333,7 @@ xfs_da3_node_remove(
         * Copy over the offending entry, or just zero it out.
         */
        index = drop_blk->index;
-       btree = xfs_da3_node_tree_p(node);
+       btree = dp->d_ops->node_tree_p(node);
        if (index < nodehdr.count - 1) {
                tmp  = nodehdr.count - index - 1;
                tmp *= (uint)sizeof(xfs_da_node_entry_t);
@@ -1385,9 +1346,9 @@ xfs_da3_node_remove(
        xfs_trans_log_buf(state->args->trans, drop_blk->bp,
            XFS_DA_LOGRANGE(node, &btree[index], sizeof(btree[index])));
        nodehdr.count -= 1;
-       xfs_da3_node_hdr_to_disk(node, &nodehdr);
+       dp->d_ops->node_hdr_to_disk(node, &nodehdr);
        xfs_trans_log_buf(state->args->trans, drop_blk->bp,
-           XFS_DA_LOGRANGE(node, &node->hdr, xfs_da3_node_hdr_size(node)));
+           XFS_DA_LOGRANGE(node, &node->hdr, dp->d_ops->node_hdr_size));
 
        /*
         * Copy the last hash value from the block to propagate upwards.
@@ -1414,15 +1375,16 @@ xfs_da3_node_unbalance(
        struct xfs_trans        *tp;
        int                     sindex;
        int                     tmp;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_node_unbalance(state->args);
 
        drop_node = drop_blk->bp->b_addr;
        save_node = save_blk->bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&drop_hdr, drop_node);
-       xfs_da3_node_hdr_from_disk(&save_hdr, save_node);
-       drop_btree = xfs_da3_node_tree_p(drop_node);
-       save_btree = xfs_da3_node_tree_p(save_node);
+       dp->d_ops->node_hdr_from_disk(&drop_hdr, drop_node);
+       dp->d_ops->node_hdr_from_disk(&save_hdr, save_node);
+       drop_btree = dp->d_ops->node_tree_p(drop_node);
+       save_btree = dp->d_ops->node_tree_p(save_node);
        tp = state->args->trans;
 
        /*
@@ -1456,10 +1418,10 @@ xfs_da3_node_unbalance(
        memcpy(&save_btree[sindex], &drop_btree[0], tmp);
        save_hdr.count += drop_hdr.count;
 
-       xfs_da3_node_hdr_to_disk(save_node, &save_hdr);
+       dp->d_ops->node_hdr_to_disk(save_node, &save_hdr);
        xfs_trans_log_buf(tp, save_blk->bp,
                XFS_DA_LOGRANGE(save_node, &save_node->hdr,
-                               xfs_da3_node_hdr_size(save_node)));
+                               dp->d_ops->node_hdr_size));
 
        /*
         * Save the last hashval in the remaining block for upward propagation.
@@ -1501,6 +1463,7 @@ xfs_da3_node_lookup_int(
        int                     max;
        int                     error;
        int                     retval;
+       struct xfs_inode        *dp = state->args->dp;
 
        args = state->args;
 
@@ -1536,7 +1499,8 @@ xfs_da3_node_lookup_int(
                if (blk->magic == XFS_DIR2_LEAFN_MAGIC ||
                    blk->magic == XFS_DIR3_LEAFN_MAGIC) {
                        blk->magic = XFS_DIR2_LEAFN_MAGIC;
-                       blk->hashval = xfs_dir2_leafn_lasthash(blk->bp, NULL);
+                       blk->hashval = xfs_dir2_leafn_lasthash(args->dp,
+                                                              blk->bp, NULL);
                        break;
                }
 
@@ -1547,8 +1511,8 @@ xfs_da3_node_lookup_int(
                 * Search an intermediate node for a match.
                 */
                node = blk->bp->b_addr;
-               xfs_da3_node_hdr_from_disk(&nodehdr, node);
-               btree = xfs_da3_node_tree_p(node);
+               dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+               btree = dp->d_ops->node_tree_p(node);
 
                max = nodehdr.count;
                blk->hashval = be32_to_cpu(btree[max - 1].hashval);
@@ -1643,6 +1607,7 @@ xfs_da3_node_lookup_int(
  */
 STATIC int
 xfs_da3_node_order(
+       struct xfs_inode *dp,
        struct xfs_buf  *node1_bp,
        struct xfs_buf  *node2_bp)
 {
@@ -1655,10 +1620,10 @@ xfs_da3_node_order(
 
        node1 = node1_bp->b_addr;
        node2 = node2_bp->b_addr;
-       xfs_da3_node_hdr_from_disk(&node1hdr, node1);
-       xfs_da3_node_hdr_from_disk(&node2hdr, node2);
-       btree1 = xfs_da3_node_tree_p(node1);
-       btree2 = xfs_da3_node_tree_p(node2);
+       dp->d_ops->node_hdr_from_disk(&node1hdr, node1);
+       dp->d_ops->node_hdr_from_disk(&node2hdr, node2);
+       btree1 = dp->d_ops->node_tree_p(node1);
+       btree2 = dp->d_ops->node_tree_p(node2);
 
        if (node1hdr.count > 0 && node2hdr.count > 0 &&
            ((be32_to_cpu(btree2[0].hashval) < be32_to_cpu(btree1[0].hashval)) ||
@@ -1685,6 +1650,7 @@ xfs_da3_blk_link(
        struct xfs_buf          *bp;
        int                     before = 0;
        int                     error;
+       struct xfs_inode        *dp = state->args->dp;
 
        /*
         * Set up environment.
@@ -1702,10 +1668,10 @@ xfs_da3_blk_link(
                before = xfs_attr_leaf_order(old_blk->bp, new_blk->bp);
                break;
        case XFS_DIR2_LEAFN_MAGIC:
-               before = xfs_dir2_leafn_order(old_blk->bp, new_blk->bp);
+               before = xfs_dir2_leafn_order(dp, old_blk->bp, new_blk->bp);
                break;
        case XFS_DA_NODE_MAGIC:
-               before = xfs_da3_node_order(old_blk->bp, new_blk->bp);
+               before = xfs_da3_node_order(dp, old_blk->bp, new_blk->bp);
                break;
        }
 
@@ -1720,7 +1686,7 @@ xfs_da3_blk_link(
                new_info->forw = cpu_to_be32(old_blk->blkno);
                new_info->back = old_info->back;
                if (old_info->back) {
-                       error = xfs_da3_node_read(args->trans, args->dp,
+                       error = xfs_da3_node_read(args->trans, dp,
                                                be32_to_cpu(old_info->back),
                                                -1, &bp, args->whichfork);
                        if (error)
@@ -1741,7 +1707,7 @@ xfs_da3_blk_link(
                new_info->forw = old_info->forw;
                new_info->back = cpu_to_be32(old_blk->blkno);
                if (old_info->forw) {
-                       error = xfs_da3_node_read(args->trans, args->dp,
+                       error = xfs_da3_node_read(args->trans, dp,
                                                be32_to_cpu(old_info->forw),
                                                -1, &bp, args->whichfork);
                        if (error)
@@ -1861,6 +1827,7 @@ xfs_da3_path_shift(
        xfs_dablk_t             blkno = 0;
        int                     level;
        int                     error;
+       struct xfs_inode        *dp = state->args->dp;
 
        trace_xfs_da_path_shift(state->args);
 
@@ -1876,8 +1843,8 @@ xfs_da3_path_shift(
        level = (path->active-1) - 1;   /* skip bottom layer in path */
        for (blk = &path->blk[level]; level >= 0; blk--, level--) {
                node = blk->bp->b_addr;
-               xfs_da3_node_hdr_from_disk(&nodehdr, node);
-               btree = xfs_da3_node_tree_p(node);
+               dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+               btree = dp->d_ops->node_tree_p(node);
 
                if (forward && (blk->index < nodehdr.count - 1)) {
                        blk->index++;
@@ -1911,7 +1878,7 @@ xfs_da3_path_shift(
                 * Read the next child block.
                 */
                blk->blkno = blkno;
-               error = xfs_da3_node_read(args->trans, args->dp, blkno, -1,
+               error = xfs_da3_node_read(args->trans, dp, blkno, -1,
                                        &blk->bp, args->whichfork);
                if (error)
                        return(error);
@@ -1933,8 +1900,8 @@ xfs_da3_path_shift(
                case XFS_DA3_NODE_MAGIC:
                        blk->magic = XFS_DA_NODE_MAGIC;
                        node = (xfs_da_intnode_t *)info;
-                       xfs_da3_node_hdr_from_disk(&nodehdr, node);
-                       btree = xfs_da3_node_tree_p(node);
+                       dp->d_ops->node_hdr_from_disk(&nodehdr, node);
+                       btree = dp->d_ops->node_tree_p(node);
                        blk->hashval = be32_to_cpu(btree[nodehdr.count - 1].hashval);
                        if (forward)
                                blk->index = 0;
@@ -1947,16 +1914,15 @@ xfs_da3_path_shift(
                        blk->magic = XFS_ATTR_LEAF_MAGIC;
                        ASSERT(level == path->active-1);
                        blk->index = 0;
-                       blk->hashval = xfs_attr_leaf_lasthash(blk->bp,
-                                                             NULL);
+                       blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL);
                        break;
                case XFS_DIR2_LEAFN_MAGIC:
                case XFS_DIR3_LEAFN_MAGIC:
                        blk->magic = XFS_DIR2_LEAFN_MAGIC;
                        ASSERT(level == path->active-1);
                        blk->index = 0;
-                       blk->hashval = xfs_dir2_leafn_lasthash(blk->bp,
-                                                              NULL);
+                       blk->hashval = xfs_dir2_leafn_lasthash(args->dp,
+                                                              blk->bp, NULL);
                        break;
                default:
                        ASSERT(0);
@@ -2163,7 +2129,7 @@ xfs_da3_swap_lastblock(
        struct xfs_dir2_leaf    *dead_leaf2;
        struct xfs_da_node_entry *btree;
        struct xfs_da3_icnode_hdr par_hdr;
-       struct xfs_inode        *ip;
+       struct xfs_inode        *dp;
        struct xfs_trans        *tp;
        struct xfs_mount        *mp;
        struct xfs_buf          *dead_buf;
@@ -2187,12 +2153,12 @@ xfs_da3_swap_lastblock(
        dead_buf = *dead_bufp;
        dead_blkno = *dead_blknop;
        tp = args->trans;
-       ip = args->dp;
+       dp = args->dp;
        w = args->whichfork;
        ASSERT(w == XFS_DATA_FORK);
-       mp = ip->i_mount;
+       mp = dp->i_mount;
        lastoff = mp->m_dirfreeblk;
-       error = xfs_bmap_last_before(tp, ip, &lastoff, w);
+       error = xfs_bmap_last_before(tp, dp, &lastoff, w);
        if (error)
                return error;
        if (unlikely(lastoff == 0)) {
@@ -2204,7 +2170,7 @@ xfs_da3_swap_lastblock(
         * Read the last block in the btree space.
         */
        last_blkno = (xfs_dablk_t)lastoff - mp->m_dirblkfsbs;
-       error = xfs_da3_node_read(tp, ip, last_blkno, -1, &last_buf, w);
+       error = xfs_da3_node_read(tp, dp, last_blkno, -1, &last_buf, w);
        if (error)
                return error;
        /*
@@ -2222,16 +2188,16 @@ xfs_da3_swap_lastblock(
                struct xfs_dir2_leaf_entry *ents;
 
                dead_leaf2 = (xfs_dir2_leaf_t *)dead_info;
-               xfs_dir3_leaf_hdr_from_disk(&leafhdr, dead_leaf2);
-               ents = xfs_dir3_leaf_ents_p(dead_leaf2);
+               dp->d_ops->leaf_hdr_from_disk(&leafhdr, dead_leaf2);
+               ents = dp->d_ops->leaf_ents_p(dead_leaf2);
                dead_level = 0;
                dead_hash = be32_to_cpu(ents[leafhdr.count - 1].hashval);
        } else {
                struct xfs_da3_icnode_hdr deadhdr;
 
                dead_node = (xfs_da_intnode_t *)dead_info;
-               xfs_da3_node_hdr_from_disk(&deadhdr, dead_node);
-               btree = xfs_da3_node_tree_p(dead_node);
+               dp->d_ops->node_hdr_from_disk(&deadhdr, dead_node);
+               btree = dp->d_ops->node_tree_p(dead_node);
                dead_level = deadhdr.level;
                dead_hash = be32_to_cpu(btree[deadhdr.count - 1].hashval);
        }
@@ -2240,7 +2206,7 @@ xfs_da3_swap_lastblock(
         * If the moved block has a left sibling, fix up the pointers.
         */
        if ((sib_blkno = be32_to_cpu(dead_info->back))) {
-               error = xfs_da3_node_read(tp, ip, sib_blkno, -1, &sib_buf, w);
+               error = xfs_da3_node_read(tp, dp, sib_blkno, -1, &sib_buf, w);
                if (error)
                        goto done;
                sib_info = sib_buf->b_addr;
@@ -2262,7 +2228,7 @@ xfs_da3_swap_lastblock(
         * If the moved block has a right sibling, fix up the pointers.
         */
        if ((sib_blkno = be32_to_cpu(dead_info->forw))) {
-               error = xfs_da3_node_read(tp, ip, sib_blkno, -1, &sib_buf, w);
+               error = xfs_da3_node_read(tp, dp, sib_blkno, -1, &sib_buf, w);
                if (error)
                        goto done;
                sib_info = sib_buf->b_addr;
@@ -2286,11 +2252,11 @@ xfs_da3_swap_lastblock(
         * Walk down the tree looking for the parent of the moved block.
         */
        for (;;) {
-               error = xfs_da3_node_read(tp, ip, par_blkno, -1, &par_buf, w);
+               error = xfs_da3_node_read(tp, dp, par_blkno, -1, &par_buf, w);
                if (error)
                        goto done;
                par_node = par_buf->b_addr;
-               xfs_da3_node_hdr_from_disk(&par_hdr, par_node);
+               dp->d_ops->node_hdr_from_disk(&par_hdr, par_node);
                if (level >= 0 && level != par_hdr.level + 1) {
                        XFS_ERROR_REPORT("xfs_da_swap_lastblock(4)",
                                         XFS_ERRLEVEL_LOW, mp);
@@ -2298,7 +2264,7 @@ xfs_da3_swap_lastblock(
                        goto done;
                }
                level = par_hdr.level;
-               btree = xfs_da3_node_tree_p(par_node);
+               btree = dp->d_ops->node_tree_p(par_node);
                for (entno = 0;
                     entno < par_hdr.count &&
                     be32_to_cpu(btree[entno].hashval) < dead_hash;
@@ -2337,18 +2303,18 @@ xfs_da3_swap_lastblock(
                        error = XFS_ERROR(EFSCORRUPTED);
                        goto done;
                }
-               error = xfs_da3_node_read(tp, ip, par_blkno, -1, &par_buf, w);
+               error = xfs_da3_node_read(tp, dp, par_blkno, -1, &par_buf, w);
                if (error)
                        goto done;
                par_node = par_buf->b_addr;
-               xfs_da3_node_hdr_from_disk(&par_hdr, par_node);
+               dp->d_ops->node_hdr_from_disk(&par_hdr, par_node);
                if (par_hdr.level != level) {
                        XFS_ERROR_REPORT("xfs_da_swap_lastblock(7)",
                                         XFS_ERRLEVEL_LOW, mp);
                        error = XFS_ERROR(EFSCORRUPTED);
                        goto done;
                }
-               btree = xfs_da3_node_tree_p(par_node);
+               btree = dp->d_ops->node_tree_p(par_node);
                entno = 0;
        }
        /*
index b1f267995dea32e97dc041c7dd14af77e1630dc8..6e95ea79f5d73aa2447fc49eb0e4c7e9bb73a25d 100644 (file)
@@ -23,146 +23,7 @@ struct xfs_bmap_free;
 struct xfs_inode;
 struct xfs_trans;
 struct zone;
-
-/*========================================================================
- * Directory Structure when greater than XFS_LBSIZE(mp) bytes.
- *========================================================================*/
-
-/*
- * This structure is common to both leaf nodes and non-leaf nodes in the Btree.
- *
- * It is used to manage a doubly linked list of all blocks at the same
- * level in the Btree, and to identify which type of block this is.
- */
-#define XFS_DA_NODE_MAGIC      0xfebe  /* magic number: non-leaf blocks */
-#define XFS_ATTR_LEAF_MAGIC    0xfbee  /* magic number: attribute leaf blks */
-#define        XFS_DIR2_LEAF1_MAGIC    0xd2f1  /* magic number: v2 dirlf single blks */
-#define        XFS_DIR2_LEAFN_MAGIC    0xd2ff  /* magic number: v2 dirlf multi blks */
-
-typedef struct xfs_da_blkinfo {
-       __be32          forw;                   /* previous block in list */
-       __be32          back;                   /* following block in list */
-       __be16          magic;                  /* validity check on block */
-       __be16          pad;                    /* unused */
-} xfs_da_blkinfo_t;
-
-/*
- * CRC enabled directory structure types
- *
- * The headers change size for the additional verification information, but
- * otherwise the tree layouts and contents are unchanged. Hence the da btree
- * code can use the struct xfs_da_blkinfo for manipulating the tree links and
- * magic numbers without modification for both v2 and v3 nodes.
- */
-#define XFS_DA3_NODE_MAGIC     0x3ebe  /* magic number: non-leaf blocks */
-#define XFS_ATTR3_LEAF_MAGIC   0x3bee  /* magic number: attribute leaf blks */
-#define        XFS_DIR3_LEAF1_MAGIC    0x3df1  /* magic number: v2 dirlf single blks */
-#define        XFS_DIR3_LEAFN_MAGIC    0x3dff  /* magic number: v2 dirlf multi blks */
-
-struct xfs_da3_blkinfo {
-       /*
-        * the node link manipulation code relies on the fact that the first
-        * element of this structure is the struct xfs_da_blkinfo so it can
-        * ignore the differences in the rest of the structures.
-        */
-       struct xfs_da_blkinfo   hdr;
-       __be32                  crc;    /* CRC of block */
-       __be64                  blkno;  /* first block of the buffer */
-       __be64                  lsn;    /* sequence number of last write */
-       uuid_t                  uuid;   /* filesystem we belong to */
-       __be64                  owner;  /* inode that owns the block */
-};
-
-/*
- * This is the structure of the root and intermediate nodes in the Btree.
- * The leaf nodes are defined above.
- *
- * Entries are not packed.
- *
- * Since we have duplicate keys, use a binary search but always follow
- * all match in the block, not just the first match found.
- */
-#define        XFS_DA_NODE_MAXDEPTH    5       /* max depth of Btree */
-
-typedef struct xfs_da_node_hdr {
-       struct xfs_da_blkinfo   info;   /* block type, links, etc. */
-       __be16                  __count; /* count of active entries */
-       __be16                  __level; /* level above leaves (leaf == 0) */
-} xfs_da_node_hdr_t;
-
-struct xfs_da3_node_hdr {
-       struct xfs_da3_blkinfo  info;   /* block type, links, etc. */
-       __be16                  __count; /* count of active entries */
-       __be16                  __level; /* level above leaves (leaf == 0) */
-       __be32                  __pad32;
-};
-
-#define XFS_DA3_NODE_CRC_OFF   (offsetof(struct xfs_da3_node_hdr, info.crc))
-
-typedef struct xfs_da_node_entry {
-       __be32  hashval;        /* hash value for this descendant */
-       __be32  before;         /* Btree block before this key */
-} xfs_da_node_entry_t;
-
-typedef struct xfs_da_intnode {
-       struct xfs_da_node_hdr  hdr;
-       struct xfs_da_node_entry __btree[];
-} xfs_da_intnode_t;
-
-struct xfs_da3_intnode {
-       struct xfs_da3_node_hdr hdr;
-       struct xfs_da_node_entry __btree[];
-};
-
-/*
- * In-core version of the node header to abstract the differences in the v2 and
- * v3 disk format of the headers. Callers need to convert to/from disk format as
- * appropriate.
- */
-struct xfs_da3_icnode_hdr {
-       __uint32_t      forw;
-       __uint32_t      back;
-       __uint16_t      magic;
-       __uint16_t      count;
-       __uint16_t      level;
-};
-
-extern void xfs_da3_node_hdr_from_disk(struct xfs_da3_icnode_hdr *to,
-                                      struct xfs_da_intnode *from);
-extern void xfs_da3_node_hdr_to_disk(struct xfs_da_intnode *to,
-                                    struct xfs_da3_icnode_hdr *from);
-
-static inline int
-__xfs_da3_node_hdr_size(bool v3)
-{
-       if (v3)
-               return sizeof(struct xfs_da3_node_hdr);
-       return sizeof(struct xfs_da_node_hdr);
-}
-static inline int
-xfs_da3_node_hdr_size(struct xfs_da_intnode *dap)
-{
-       bool    v3 = dap->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC);
-
-       return __xfs_da3_node_hdr_size(v3);
-}
-
-static inline struct xfs_da_node_entry *
-xfs_da3_node_tree_p(struct xfs_da_intnode *dap)
-{
-       if (dap->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC)) {
-               struct xfs_da3_intnode *dap3 = (struct xfs_da3_intnode *)dap;
-               return dap3->__btree;
-       }
-       return dap->__btree;
-}
-
-extern void xfs_da3_intnode_from_disk(struct xfs_da3_icnode_hdr *to,
-                                     struct xfs_da_intnode *from);
-extern void xfs_da3_intnode_to_disk(struct xfs_da_intnode *to,
-                                   struct xfs_da3_icnode_hdr *from);
-
-#define        XFS_LBSIZE(mp)  (mp)->m_sb.sb_blocksize
+struct xfs_dir_ops;
 
 /*========================================================================
  * Btree searching and modification structure definitions.
@@ -309,8 +170,6 @@ int xfs_da3_node_read(struct xfs_trans *tp, struct xfs_inode *dp,
                         xfs_dablk_t bno, xfs_daddr_t mappedbno,
                         struct xfs_buf **bpp, int which_fork);
 
-extern const struct xfs_buf_ops xfs_da3_node_buf_ops;
-
 /*
  * Utility routines.
  */
diff --git a/fs/xfs/xfs_da_format.c b/fs/xfs/xfs_da_format.c
new file mode 100644 (file)
index 0000000..e6c83e1
--- /dev/null
@@ -0,0 +1,907 @@
+/*
+ * Copyright (c) 2000,2002,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_mount.h"
+#include "xfs_da_format.h"
+#include "xfs_inode.h"
+#include "xfs_dir2.h"
+
+/*
+ * Shortform directory ops
+ */
+static int
+xfs_dir2_sf_entsize(
+       struct xfs_dir2_sf_hdr  *hdr,
+       int                     len)
+{
+       int count = sizeof(struct xfs_dir2_sf_entry);   /* namelen + offset */
+
+       count += len;                                   /* name */
+       count += hdr->i8count ? sizeof(xfs_dir2_ino8_t) :
+                               sizeof(xfs_dir2_ino4_t); /* ino # */
+       return count;
+}
+
+static int
+xfs_dir3_sf_entsize(
+       struct xfs_dir2_sf_hdr  *hdr,
+       int                     len)
+{
+       return xfs_dir2_sf_entsize(hdr, len) + sizeof(__uint8_t);
+}
+
+static struct xfs_dir2_sf_entry *
+xfs_dir2_sf_nextentry(
+       struct xfs_dir2_sf_hdr  *hdr,
+       struct xfs_dir2_sf_entry *sfep)
+{
+       return (struct xfs_dir2_sf_entry *)
+               ((char *)sfep + xfs_dir2_sf_entsize(hdr, sfep->namelen));
+}
+
+static struct xfs_dir2_sf_entry *
+xfs_dir3_sf_nextentry(
+       struct xfs_dir2_sf_hdr  *hdr,
+       struct xfs_dir2_sf_entry *sfep)
+{
+       return (struct xfs_dir2_sf_entry *)
+               ((char *)sfep + xfs_dir3_sf_entsize(hdr, sfep->namelen));
+}
+
+
+/*
+ * For filetype enabled shortform directories, the file type field is stored at
+ * the end of the name.  Because it's only a single byte, endian conversion is
+ * not necessary. For non-filetype enable directories, the type is always
+ * unknown and we never store the value.
+ */
+static __uint8_t
+xfs_dir2_sfe_get_ftype(
+       struct xfs_dir2_sf_entry *sfep)
+{
+       return XFS_DIR3_FT_UNKNOWN;
+}
+
+static void
+xfs_dir2_sfe_put_ftype(
+       struct xfs_dir2_sf_entry *sfep,
+       __uint8_t               ftype)
+{
+       ASSERT(ftype < XFS_DIR3_FT_MAX);
+}
+
+static __uint8_t
+xfs_dir3_sfe_get_ftype(
+       struct xfs_dir2_sf_entry *sfep)
+{
+       __uint8_t       ftype;
+
+       ftype = sfep->name[sfep->namelen];
+       if (ftype >= XFS_DIR3_FT_MAX)
+               return XFS_DIR3_FT_UNKNOWN;
+       return ftype;
+}
+
+static void
+xfs_dir3_sfe_put_ftype(
+       struct xfs_dir2_sf_entry *sfep,
+       __uint8_t               ftype)
+{
+       ASSERT(ftype < XFS_DIR3_FT_MAX);
+
+       sfep->name[sfep->namelen] = ftype;
+}
+
+/*
+ * Inode numbers in short-form directories can come in two versions,
+ * either 4 bytes or 8 bytes wide.  These helpers deal with the
+ * two forms transparently by looking at the headers i8count field.
+ *
+ * For 64-bit inode number the most significant byte must be zero.
+ */
+static xfs_ino_t
+xfs_dir2_sf_get_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       xfs_dir2_inou_t         *from)
+{
+       if (hdr->i8count)
+               return get_unaligned_be64(&from->i8.i) & 0x00ffffffffffffffULL;
+       else
+               return get_unaligned_be32(&from->i4.i);
+}
+
+static void
+xfs_dir2_sf_put_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       xfs_dir2_inou_t         *to,
+       xfs_ino_t               ino)
+{
+       ASSERT((ino & 0xff00000000000000ULL) == 0);
+
+       if (hdr->i8count)
+               put_unaligned_be64(ino, &to->i8.i);
+       else
+               put_unaligned_be32(ino, &to->i4.i);
+}
+
+static xfs_ino_t
+xfs_dir2_sf_get_parent_ino(
+       struct xfs_dir2_sf_hdr  *hdr)
+{
+       return xfs_dir2_sf_get_ino(hdr, &hdr->parent);
+}
+
+static void
+xfs_dir2_sf_put_parent_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       xfs_ino_t               ino)
+{
+       xfs_dir2_sf_put_ino(hdr, &hdr->parent, ino);
+}
+
+/*
+ * In short-form directory entries the inode numbers are stored at variable
+ * offset behind the entry name. If the entry stores a filetype value, then it
+ * sits between the name and the inode number. Hence the inode numbers may only
+ * be accessed through the helpers below.
+ */
+static xfs_ino_t
+xfs_dir2_sfe_get_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       struct xfs_dir2_sf_entry *sfep)
+{
+       return xfs_dir2_sf_get_ino(hdr,
+                               (xfs_dir2_inou_t *)&sfep->name[sfep->namelen]);
+}
+
+static void
+xfs_dir2_sfe_put_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       struct xfs_dir2_sf_entry *sfep,
+       xfs_ino_t               ino)
+{
+       xfs_dir2_sf_put_ino(hdr,
+                           (xfs_dir2_inou_t *)&sfep->name[sfep->namelen], ino);
+}
+
+static xfs_ino_t
+xfs_dir3_sfe_get_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       struct xfs_dir2_sf_entry *sfep)
+{
+       return xfs_dir2_sf_get_ino(hdr,
+                       (xfs_dir2_inou_t *)&sfep->name[sfep->namelen + 1]);
+}
+
+static void
+xfs_dir3_sfe_put_ino(
+       struct xfs_dir2_sf_hdr  *hdr,
+       struct xfs_dir2_sf_entry *sfep,
+       xfs_ino_t               ino)
+{
+       xfs_dir2_sf_put_ino(hdr,
+                       (xfs_dir2_inou_t *)&sfep->name[sfep->namelen + 1], ino);
+}
+
+
+/*
+ * Directory data block operations
+ */
+
+/*
+ * For special situations, the dirent size ends up fixed because we always know
+ * what the size of the entry is. That's true for the "." and "..", and
+ * therefore we know that they are a fixed size and hence their offsets are
+ * constant, as is the first entry.
+ *
+ * Hence, this calculation is written as a macro to be able to be calculated at
+ * compile time and so certain offsets can be calculated directly in the
+ * structure initaliser via the macro. There are two macros - one for dirents
+ * with ftype and without so there are no unresolvable conditionals in the
+ * calculations. We also use round_up() as XFS_DIR2_DATA_ALIGN is always a power
+ * of 2 and the compiler doesn't reject it (unlike roundup()).
+ */
+#define XFS_DIR2_DATA_ENTSIZE(n)                                       \
+       round_up((offsetof(struct xfs_dir2_data_entry, name[0]) + (n) + \
+                sizeof(xfs_dir2_data_off_t)), XFS_DIR2_DATA_ALIGN)
+
+#define XFS_DIR3_DATA_ENTSIZE(n)                                       \
+       round_up((offsetof(struct xfs_dir2_data_entry, name[0]) + (n) + \
+                sizeof(xfs_dir2_data_off_t) + sizeof(__uint8_t)),      \
+               XFS_DIR2_DATA_ALIGN)
+
+static int
+xfs_dir2_data_entsize(
+       int                     n)
+{
+       return XFS_DIR2_DATA_ENTSIZE(n);
+}
+
+static int
+xfs_dir3_data_entsize(
+       int                     n)
+{
+       return XFS_DIR3_DATA_ENTSIZE(n);
+}
+
+static __uint8_t
+xfs_dir2_data_get_ftype(
+       struct xfs_dir2_data_entry *dep)
+{
+       return XFS_DIR3_FT_UNKNOWN;
+}
+
+static void
+xfs_dir2_data_put_ftype(
+       struct xfs_dir2_data_entry *dep,
+       __uint8_t               ftype)
+{
+       ASSERT(ftype < XFS_DIR3_FT_MAX);
+}
+
+static __uint8_t
+xfs_dir3_data_get_ftype(
+       struct xfs_dir2_data_entry *dep)
+{
+       __uint8_t       ftype = dep->name[dep->namelen];
+
+       ASSERT(ftype < XFS_DIR3_FT_MAX);
+       if (ftype >= XFS_DIR3_FT_MAX)
+               return XFS_DIR3_FT_UNKNOWN;
+       return ftype;
+}
+
+static void
+xfs_dir3_data_put_ftype(
+       struct xfs_dir2_data_entry *dep,
+       __uint8_t               type)
+{
+       ASSERT(type < XFS_DIR3_FT_MAX);
+       ASSERT(dep->namelen != 0);
+
+       dep->name[dep->namelen] = type;
+}
+
+/*
+ * Pointer to an entry's tag word.
+ */
+static __be16 *
+xfs_dir2_data_entry_tag_p(
+       struct xfs_dir2_data_entry *dep)
+{
+       return (__be16 *)((char *)dep +
+               xfs_dir2_data_entsize(dep->namelen) - sizeof(__be16));
+}
+
+static __be16 *
+xfs_dir3_data_entry_tag_p(
+       struct xfs_dir2_data_entry *dep)
+{
+       return (__be16 *)((char *)dep +
+               xfs_dir3_data_entsize(dep->namelen) - sizeof(__be16));
+}
+
+/*
+ * location of . and .. in data space (always block 0)
+ */
+static struct xfs_dir2_data_entry *
+xfs_dir2_data_dot_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir2_data_dotdot_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR2_DATA_ENTSIZE(1));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir2_data_first_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR2_DATA_ENTSIZE(1) +
+                               XFS_DIR2_DATA_ENTSIZE(2));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir2_ftype_data_dotdot_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir2_ftype_data_first_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1) +
+                               XFS_DIR3_DATA_ENTSIZE(2));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir3_data_dot_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir3_data_dotdot_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir3_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir3_data_first_entry_p(
+       struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir3_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1) +
+                               XFS_DIR3_DATA_ENTSIZE(2));
+}
+
+static struct xfs_dir2_data_free *
+xfs_dir2_data_bestfree_p(struct xfs_dir2_data_hdr *hdr)
+{
+       return hdr->bestfree;
+}
+
+static struct xfs_dir2_data_free *
+xfs_dir3_data_bestfree_p(struct xfs_dir2_data_hdr *hdr)
+{
+       return ((struct xfs_dir3_data_hdr *)hdr)->best_free;
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir2_data_entry_p(struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
+}
+
+static struct xfs_dir2_data_unused *
+xfs_dir2_data_unused_p(struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_unused *)
+               ((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
+}
+
+static struct xfs_dir2_data_entry *
+xfs_dir3_data_entry_p(struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_entry *)
+               ((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
+}
+
+static struct xfs_dir2_data_unused *
+xfs_dir3_data_unused_p(struct xfs_dir2_data_hdr *hdr)
+{
+       return (struct xfs_dir2_data_unused *)
+               ((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
+}
+
+
+/*
+ * Directory Leaf block operations
+ */
+static int
+xfs_dir2_max_leaf_ents(struct xfs_mount *mp)
+{
+       return (mp->m_dirblksize - sizeof(struct xfs_dir2_leaf_hdr)) /
+               (uint)sizeof(struct xfs_dir2_leaf_entry);
+}
+
+static struct xfs_dir2_leaf_entry *
+xfs_dir2_leaf_ents_p(struct xfs_dir2_leaf *lp)
+{
+       return lp->__ents;
+}
+
+static int
+xfs_dir3_max_leaf_ents(struct xfs_mount *mp)
+{
+       return (mp->m_dirblksize - sizeof(struct xfs_dir3_leaf_hdr)) /
+               (uint)sizeof(struct xfs_dir2_leaf_entry);
+}
+
+static struct xfs_dir2_leaf_entry *
+xfs_dir3_leaf_ents_p(struct xfs_dir2_leaf *lp)
+{
+       return ((struct xfs_dir3_leaf *)lp)->__ents;
+}
+
+static void
+xfs_dir2_leaf_hdr_from_disk(
+       struct xfs_dir3_icleaf_hdr      *to,
+       struct xfs_dir2_leaf            *from)
+{
+       to->forw = be32_to_cpu(from->hdr.info.forw);
+       to->back = be32_to_cpu(from->hdr.info.back);
+       to->magic = be16_to_cpu(from->hdr.info.magic);
+       to->count = be16_to_cpu(from->hdr.count);
+       to->stale = be16_to_cpu(from->hdr.stale);
+
+       ASSERT(to->magic == XFS_DIR2_LEAF1_MAGIC ||
+              to->magic == XFS_DIR2_LEAFN_MAGIC);
+}
+
+static void
+xfs_dir2_leaf_hdr_to_disk(
+       struct xfs_dir2_leaf            *to,
+       struct xfs_dir3_icleaf_hdr      *from)
+{
+       ASSERT(from->magic == XFS_DIR2_LEAF1_MAGIC ||
+              from->magic == XFS_DIR2_LEAFN_MAGIC);
+
+       to->hdr.info.forw = cpu_to_be32(from->forw);
+       to->hdr.info.back = cpu_to_be32(from->back);
+       to->hdr.info.magic = cpu_to_be16(from->magic);
+       to->hdr.count = cpu_to_be16(from->count);
+       to->hdr.stale = cpu_to_be16(from->stale);
+}
+
+static void
+xfs_dir3_leaf_hdr_from_disk(
+       struct xfs_dir3_icleaf_hdr      *to,
+       struct xfs_dir2_leaf            *from)
+{
+       struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)from;
+
+       to->forw = be32_to_cpu(hdr3->info.hdr.forw);
+       to->back = be32_to_cpu(hdr3->info.hdr.back);
+       to->magic = be16_to_cpu(hdr3->info.hdr.magic);
+       to->count = be16_to_cpu(hdr3->count);
+       to->stale = be16_to_cpu(hdr3->stale);
+
+       ASSERT(to->magic == XFS_DIR3_LEAF1_MAGIC ||
+              to->magic == XFS_DIR3_LEAFN_MAGIC);
+}
+
+static void
+xfs_dir3_leaf_hdr_to_disk(
+       struct xfs_dir2_leaf            *to,
+       struct xfs_dir3_icleaf_hdr      *from)
+{
+       struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)to;
+
+       ASSERT(from->magic == XFS_DIR3_LEAF1_MAGIC ||
+              from->magic == XFS_DIR3_LEAFN_MAGIC);
+
+       hdr3->info.hdr.forw = cpu_to_be32(from->forw);
+       hdr3->info.hdr.back = cpu_to_be32(from->back);
+       hdr3->info.hdr.magic = cpu_to_be16(from->magic);
+       hdr3->count = cpu_to_be16(from->count);
+       hdr3->stale = cpu_to_be16(from->stale);
+}
+
+
+/*
+ * Directory/Attribute Node block operations
+ */
+static struct xfs_da_node_entry *
+xfs_da2_node_tree_p(struct xfs_da_intnode *dap)
+{
+       return dap->__btree;
+}
+
+static struct xfs_da_node_entry *
+xfs_da3_node_tree_p(struct xfs_da_intnode *dap)
+{
+       return ((struct xfs_da3_intnode *)dap)->__btree;
+}
+
+static void
+xfs_da2_node_hdr_from_disk(
+       struct xfs_da3_icnode_hdr       *to,
+       struct xfs_da_intnode           *from)
+{
+       ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
+       to->forw = be32_to_cpu(from->hdr.info.forw);
+       to->back = be32_to_cpu(from->hdr.info.back);
+       to->magic = be16_to_cpu(from->hdr.info.magic);
+       to->count = be16_to_cpu(from->hdr.__count);
+       to->level = be16_to_cpu(from->hdr.__level);
+}
+
+static void
+xfs_da2_node_hdr_to_disk(
+       struct xfs_da_intnode           *to,
+       struct xfs_da3_icnode_hdr       *from)
+{
+       ASSERT(from->magic == XFS_DA_NODE_MAGIC);
+       to->hdr.info.forw = cpu_to_be32(from->forw);
+       to->hdr.info.back = cpu_to_be32(from->back);
+       to->hdr.info.magic = cpu_to_be16(from->magic);
+       to->hdr.__count = cpu_to_be16(from->count);
+       to->hdr.__level = cpu_to_be16(from->level);
+}
+
+static void
+xfs_da3_node_hdr_from_disk(
+       struct xfs_da3_icnode_hdr       *to,
+       struct xfs_da_intnode           *from)
+{
+       struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)from;
+
+       ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC));
+       to->forw = be32_to_cpu(hdr3->info.hdr.forw);
+       to->back = be32_to_cpu(hdr3->info.hdr.back);
+       to->magic = be16_to_cpu(hdr3->info.hdr.magic);
+       to->count = be16_to_cpu(hdr3->__count);
+       to->level = be16_to_cpu(hdr3->__level);
+}
+
+static void
+xfs_da3_node_hdr_to_disk(
+       struct xfs_da_intnode           *to,
+       struct xfs_da3_icnode_hdr       *from)
+{
+       struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)to;
+
+       ASSERT(from->magic == XFS_DA3_NODE_MAGIC);
+       hdr3->info.hdr.forw = cpu_to_be32(from->forw);
+       hdr3->info.hdr.back = cpu_to_be32(from->back);
+       hdr3->info.hdr.magic = cpu_to_be16(from->magic);
+       hdr3->__count = cpu_to_be16(from->count);
+       hdr3->__level = cpu_to_be16(from->level);
+}
+
+
+/*
+ * Directory free space block operations
+ */
+static int
+xfs_dir2_free_max_bests(struct xfs_mount *mp)
+{
+       return (mp->m_dirblksize - sizeof(struct xfs_dir2_free_hdr)) /
+               sizeof(xfs_dir2_data_off_t);
+}
+
+static __be16 *
+xfs_dir2_free_bests_p(struct xfs_dir2_free *free)
+{
+       return (__be16 *)((char *)free + sizeof(struct xfs_dir2_free_hdr));
+}
+
+/*
+ * Convert data space db to the corresponding free db.
+ */
+static xfs_dir2_db_t
+xfs_dir2_db_to_fdb(struct xfs_mount *mp, xfs_dir2_db_t db)
+{
+       return XFS_DIR2_FREE_FIRSTDB(mp) + db / xfs_dir2_free_max_bests(mp);
+}
+
+/*
+ * Convert data space db to the corresponding index in a free db.
+ */
+static int
+xfs_dir2_db_to_fdindex(struct xfs_mount *mp, xfs_dir2_db_t db)
+{
+       return db % xfs_dir2_free_max_bests(mp);
+}
+
+static int
+xfs_dir3_free_max_bests(struct xfs_mount *mp)
+{
+       return (mp->m_dirblksize - sizeof(struct xfs_dir3_free_hdr)) /
+               sizeof(xfs_dir2_data_off_t);
+}
+
+static __be16 *
+xfs_dir3_free_bests_p(struct xfs_dir2_free *free)
+{
+       return (__be16 *)((char *)free + sizeof(struct xfs_dir3_free_hdr));
+}
+
+/*
+ * Convert data space db to the corresponding free db.
+ */
+static xfs_dir2_db_t
+xfs_dir3_db_to_fdb(struct xfs_mount *mp, xfs_dir2_db_t db)
+{
+       return XFS_DIR2_FREE_FIRSTDB(mp) + db / xfs_dir3_free_max_bests(mp);
+}
+
+/*
+ * Convert data space db to the corresponding index in a free db.
+ */
+static int
+xfs_dir3_db_to_fdindex(struct xfs_mount *mp, xfs_dir2_db_t db)
+{
+       return db % xfs_dir3_free_max_bests(mp);
+}
+
+static void
+xfs_dir2_free_hdr_from_disk(
+       struct xfs_dir3_icfree_hdr      *to,
+       struct xfs_dir2_free            *from)
+{
+       to->magic = be32_to_cpu(from->hdr.magic);
+       to->firstdb = be32_to_cpu(from->hdr.firstdb);
+       to->nvalid = be32_to_cpu(from->hdr.nvalid);
+       to->nused = be32_to_cpu(from->hdr.nused);
+       ASSERT(to->magic == XFS_DIR2_FREE_MAGIC);
+}
+
+static void
+xfs_dir2_free_hdr_to_disk(
+       struct xfs_dir2_free            *to,
+       struct xfs_dir3_icfree_hdr      *from)
+{
+       ASSERT(from->magic == XFS_DIR2_FREE_MAGIC);
+
+       to->hdr.magic = cpu_to_be32(from->magic);
+       to->hdr.firstdb = cpu_to_be32(from->firstdb);
+       to->hdr.nvalid = cpu_to_be32(from->nvalid);
+       to->hdr.nused = cpu_to_be32(from->nused);
+}
+
+static void
+xfs_dir3_free_hdr_from_disk(
+       struct xfs_dir3_icfree_hdr      *to,
+       struct xfs_dir2_free            *from)
+{
+       struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)from;
+
+       to->magic = be32_to_cpu(hdr3->hdr.magic);
+       to->firstdb = be32_to_cpu(hdr3->firstdb);
+       to->nvalid = be32_to_cpu(hdr3->nvalid);
+       to->nused = be32_to_cpu(hdr3->nused);
+
+       ASSERT(to->magic == XFS_DIR3_FREE_MAGIC);
+}
+
+static void
+xfs_dir3_free_hdr_to_disk(
+       struct xfs_dir2_free            *to,
+       struct xfs_dir3_icfree_hdr      *from)
+{
+       struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)to;
+
+       ASSERT(from->magic == XFS_DIR3_FREE_MAGIC);
+
+       hdr3->hdr.magic = cpu_to_be32(from->magic);
+       hdr3->firstdb = cpu_to_be32(from->firstdb);
+       hdr3->nvalid = cpu_to_be32(from->nvalid);
+       hdr3->nused = cpu_to_be32(from->nused);
+}
+
+static const struct xfs_dir_ops xfs_dir2_ops = {
+       .sf_entsize = xfs_dir2_sf_entsize,
+       .sf_nextentry = xfs_dir2_sf_nextentry,
+       .sf_get_ftype = xfs_dir2_sfe_get_ftype,
+       .sf_put_ftype = xfs_dir2_sfe_put_ftype,
+       .sf_get_ino = xfs_dir2_sfe_get_ino,
+       .sf_put_ino = xfs_dir2_sfe_put_ino,
+       .sf_get_parent_ino = xfs_dir2_sf_get_parent_ino,
+       .sf_put_parent_ino = xfs_dir2_sf_put_parent_ino,
+
+       .data_entsize = xfs_dir2_data_entsize,
+       .data_get_ftype = xfs_dir2_data_get_ftype,
+       .data_put_ftype = xfs_dir2_data_put_ftype,
+       .data_entry_tag_p = xfs_dir2_data_entry_tag_p,
+       .data_bestfree_p = xfs_dir2_data_bestfree_p,
+
+       .data_dot_offset = sizeof(struct xfs_dir2_data_hdr),
+       .data_dotdot_offset = sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR2_DATA_ENTSIZE(1),
+       .data_first_offset =  sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR2_DATA_ENTSIZE(1) +
+                               XFS_DIR2_DATA_ENTSIZE(2),
+       .data_entry_offset = sizeof(struct xfs_dir2_data_hdr),
+
+       .data_dot_entry_p = xfs_dir2_data_dot_entry_p,
+       .data_dotdot_entry_p = xfs_dir2_data_dotdot_entry_p,
+       .data_first_entry_p = xfs_dir2_data_first_entry_p,
+       .data_entry_p = xfs_dir2_data_entry_p,
+       .data_unused_p = xfs_dir2_data_unused_p,
+
+       .leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr),
+       .leaf_hdr_to_disk = xfs_dir2_leaf_hdr_to_disk,
+       .leaf_hdr_from_disk = xfs_dir2_leaf_hdr_from_disk,
+       .leaf_max_ents = xfs_dir2_max_leaf_ents,
+       .leaf_ents_p = xfs_dir2_leaf_ents_p,
+
+       .node_hdr_size = sizeof(struct xfs_da_node_hdr),
+       .node_hdr_to_disk = xfs_da2_node_hdr_to_disk,
+       .node_hdr_from_disk = xfs_da2_node_hdr_from_disk,
+       .node_tree_p = xfs_da2_node_tree_p,
+
+       .free_hdr_size = sizeof(struct xfs_dir2_free_hdr),
+       .free_hdr_to_disk = xfs_dir2_free_hdr_to_disk,
+       .free_hdr_from_disk = xfs_dir2_free_hdr_from_disk,
+       .free_max_bests = xfs_dir2_free_max_bests,
+       .free_bests_p = xfs_dir2_free_bests_p,
+       .db_to_fdb = xfs_dir2_db_to_fdb,
+       .db_to_fdindex = xfs_dir2_db_to_fdindex,
+};
+
+static const struct xfs_dir_ops xfs_dir2_ftype_ops = {
+       .sf_entsize = xfs_dir3_sf_entsize,
+       .sf_nextentry = xfs_dir3_sf_nextentry,
+       .sf_get_ftype = xfs_dir3_sfe_get_ftype,
+       .sf_put_ftype = xfs_dir3_sfe_put_ftype,
+       .sf_get_ino = xfs_dir3_sfe_get_ino,
+       .sf_put_ino = xfs_dir3_sfe_put_ino,
+       .sf_get_parent_ino = xfs_dir2_sf_get_parent_ino,
+       .sf_put_parent_ino = xfs_dir2_sf_put_parent_ino,
+
+       .data_entsize = xfs_dir3_data_entsize,
+       .data_get_ftype = xfs_dir3_data_get_ftype,
+       .data_put_ftype = xfs_dir3_data_put_ftype,
+       .data_entry_tag_p = xfs_dir3_data_entry_tag_p,
+       .data_bestfree_p = xfs_dir2_data_bestfree_p,
+
+       .data_dot_offset = sizeof(struct xfs_dir2_data_hdr),
+       .data_dotdot_offset = sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1),
+       .data_first_offset =  sizeof(struct xfs_dir2_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1) +
+                               XFS_DIR3_DATA_ENTSIZE(2),
+       .data_entry_offset = sizeof(struct xfs_dir2_data_hdr),
+
+       .data_dot_entry_p = xfs_dir2_data_dot_entry_p,
+       .data_dotdot_entry_p = xfs_dir2_ftype_data_dotdot_entry_p,
+       .data_first_entry_p = xfs_dir2_ftype_data_first_entry_p,
+       .data_entry_p = xfs_dir2_data_entry_p,
+       .data_unused_p = xfs_dir2_data_unused_p,
+
+       .leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr),
+       .leaf_hdr_to_disk = xfs_dir2_leaf_hdr_to_disk,
+       .leaf_hdr_from_disk = xfs_dir2_leaf_hdr_from_disk,
+       .leaf_max_ents = xfs_dir2_max_leaf_ents,
+       .leaf_ents_p = xfs_dir2_leaf_ents_p,
+
+       .node_hdr_size = sizeof(struct xfs_da_node_hdr),
+       .node_hdr_to_disk = xfs_da2_node_hdr_to_disk,
+       .node_hdr_from_disk = xfs_da2_node_hdr_from_disk,
+       .node_tree_p = xfs_da2_node_tree_p,
+
+       .free_hdr_size = sizeof(struct xfs_dir2_free_hdr),
+       .free_hdr_to_disk = xfs_dir2_free_hdr_to_disk,
+       .free_hdr_from_disk = xfs_dir2_free_hdr_from_disk,
+       .free_max_bests = xfs_dir2_free_max_bests,
+       .free_bests_p = xfs_dir2_free_bests_p,
+       .db_to_fdb = xfs_dir2_db_to_fdb,
+       .db_to_fdindex = xfs_dir2_db_to_fdindex,
+};
+
+static const struct xfs_dir_ops xfs_dir3_ops = {
+       .sf_entsize = xfs_dir3_sf_entsize,
+       .sf_nextentry = xfs_dir3_sf_nextentry,
+       .sf_get_ftype = xfs_dir3_sfe_get_ftype,
+       .sf_put_ftype = xfs_dir3_sfe_put_ftype,
+       .sf_get_ino = xfs_dir3_sfe_get_ino,
+       .sf_put_ino = xfs_dir3_sfe_put_ino,
+       .sf_get_parent_ino = xfs_dir2_sf_get_parent_ino,
+       .sf_put_parent_ino = xfs_dir2_sf_put_parent_ino,
+
+       .data_entsize = xfs_dir3_data_entsize,
+       .data_get_ftype = xfs_dir3_data_get_ftype,
+       .data_put_ftype = xfs_dir3_data_put_ftype,
+       .data_entry_tag_p = xfs_dir3_data_entry_tag_p,
+       .data_bestfree_p = xfs_dir3_data_bestfree_p,
+
+       .data_dot_offset = sizeof(struct xfs_dir3_data_hdr),
+       .data_dotdot_offset = sizeof(struct xfs_dir3_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1),
+       .data_first_offset =  sizeof(struct xfs_dir3_data_hdr) +
+                               XFS_DIR3_DATA_ENTSIZE(1) +
+                               XFS_DIR3_DATA_ENTSIZE(2),
+       .data_entry_offset = sizeof(struct xfs_dir3_data_hdr),
+
+       .data_dot_entry_p = xfs_dir3_data_dot_entry_p,
+       .data_dotdot_entry_p = xfs_dir3_data_dotdot_entry_p,
+       .data_first_entry_p = xfs_dir3_data_first_entry_p,
+       .data_entry_p = xfs_dir3_data_entry_p,
+       .data_unused_p = xfs_dir3_data_unused_p,
+
+       .leaf_hdr_size = sizeof(struct xfs_dir3_leaf_hdr),
+       .leaf_hdr_to_disk = xfs_dir3_leaf_hdr_to_disk,
+       .leaf_hdr_from_disk = xfs_dir3_leaf_hdr_from_disk,
+       .leaf_max_ents = xfs_dir3_max_leaf_ents,
+       .leaf_ents_p = xfs_dir3_leaf_ents_p,
+
+       .node_hdr_size = sizeof(struct xfs_da3_node_hdr),
+       .node_hdr_to_disk = xfs_da3_node_hdr_to_disk,
+       .node_hdr_from_disk = xfs_da3_node_hdr_from_disk,
+       .node_tree_p = xfs_da3_node_tree_p,
+
+       .free_hdr_size = sizeof(struct xfs_dir3_free_hdr),
+       .free_hdr_to_disk = xfs_dir3_free_hdr_to_disk,
+       .free_hdr_from_disk = xfs_dir3_free_hdr_from_disk,
+       .free_max_bests = xfs_dir3_free_max_bests,
+       .free_bests_p = xfs_dir3_free_bests_p,
+       .db_to_fdb = xfs_dir3_db_to_fdb,
+       .db_to_fdindex = xfs_dir3_db_to_fdindex,
+};
+
+static const struct xfs_dir_ops xfs_dir2_nondir_ops = {
+       .node_hdr_size = sizeof(struct xfs_da_node_hdr),
+       .node_hdr_to_disk = xfs_da2_node_hdr_to_disk,
+       .node_hdr_from_disk = xfs_da2_node_hdr_from_disk,
+       .node_tree_p = xfs_da2_node_tree_p,
+};
+
+static const struct xfs_dir_ops xfs_dir3_nondir_ops = {
+       .node_hdr_size = sizeof(struct xfs_da3_node_hdr),
+       .node_hdr_to_disk = xfs_da3_node_hdr_to_disk,
+       .node_hdr_from_disk = xfs_da3_node_hdr_from_disk,
+       .node_tree_p = xfs_da3_node_tree_p,
+};
+
+/*
+ * Return the ops structure according to the current config.  If we are passed
+ * an inode, then that overrides the default config we use which is based on
+ * feature bits.
+ */
+const struct xfs_dir_ops *
+xfs_dir_get_ops(
+       struct xfs_mount        *mp,
+       struct xfs_inode        *dp)
+{
+       if (dp)
+               return dp->d_ops;
+       if (mp->m_dir_inode_ops)
+               return mp->m_dir_inode_ops;
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               return &xfs_dir3_ops;
+       if (xfs_sb_version_hasftype(&mp->m_sb))
+               return &xfs_dir2_ftype_ops;
+       return &xfs_dir2_ops;
+}
+
+const struct xfs_dir_ops *
+xfs_nondir_get_ops(
+       struct xfs_mount        *mp,
+       struct xfs_inode        *dp)
+{
+       if (dp)
+               return dp->d_ops;
+       if (mp->m_nondir_inode_ops)
+               return mp->m_nondir_inode_ops;
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               return &xfs_dir3_nondir_ops;
+       return &xfs_dir2_nondir_ops;
+}
similarity index 60%
rename from fs/xfs/xfs_dir2_format.h
rename to fs/xfs/xfs_da_format.h
index 9cf67381adf6769d0b6fc984b6fe5ecaf8cef5b4..a19d3f8f639cf3eb3e95d23bc52b58d5eb9b6591 100644 (file)
  * along with this program; if not, write the Free Software Foundation,
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
-#ifndef __XFS_DIR2_FORMAT_H__
-#define __XFS_DIR2_FORMAT_H__
+#ifndef __XFS_DA_FORMAT_H__
+#define __XFS_DA_FORMAT_H__
+
+/*========================================================================
+ * Directory Structure when greater than XFS_LBSIZE(mp) bytes.
+ *========================================================================*/
+
+/*
+ * This structure is common to both leaf nodes and non-leaf nodes in the Btree.
+ *
+ * It is used to manage a doubly linked list of all blocks at the same
+ * level in the Btree, and to identify which type of block this is.
+ */
+#define XFS_DA_NODE_MAGIC      0xfebe  /* magic number: non-leaf blocks */
+#define XFS_ATTR_LEAF_MAGIC    0xfbee  /* magic number: attribute leaf blks */
+#define        XFS_DIR2_LEAF1_MAGIC    0xd2f1  /* magic number: v2 dirlf single blks */
+#define        XFS_DIR2_LEAFN_MAGIC    0xd2ff  /* magic number: v2 dirlf multi blks */
+
+typedef struct xfs_da_blkinfo {
+       __be32          forw;                   /* previous block in list */
+       __be32          back;                   /* following block in list */
+       __be16          magic;                  /* validity check on block */
+       __be16          pad;                    /* unused */
+} xfs_da_blkinfo_t;
+
+/*
+ * CRC enabled directory structure types
+ *
+ * The headers change size for the additional verification information, but
+ * otherwise the tree layouts and contents are unchanged. Hence the da btree
+ * code can use the struct xfs_da_blkinfo for manipulating the tree links and
+ * magic numbers without modification for both v2 and v3 nodes.
+ */
+#define XFS_DA3_NODE_MAGIC     0x3ebe  /* magic number: non-leaf blocks */
+#define XFS_ATTR3_LEAF_MAGIC   0x3bee  /* magic number: attribute leaf blks */
+#define        XFS_DIR3_LEAF1_MAGIC    0x3df1  /* magic number: v2 dirlf single blks */
+#define        XFS_DIR3_LEAFN_MAGIC    0x3dff  /* magic number: v2 dirlf multi blks */
+
+struct xfs_da3_blkinfo {
+       /*
+        * the node link manipulation code relies on the fact that the first
+        * element of this structure is the struct xfs_da_blkinfo so it can
+        * ignore the differences in the rest of the structures.
+        */
+       struct xfs_da_blkinfo   hdr;
+       __be32                  crc;    /* CRC of block */
+       __be64                  blkno;  /* first block of the buffer */
+       __be64                  lsn;    /* sequence number of last write */
+       uuid_t                  uuid;   /* filesystem we belong to */
+       __be64                  owner;  /* inode that owns the block */
+};
+
+/*
+ * This is the structure of the root and intermediate nodes in the Btree.
+ * The leaf nodes are defined above.
+ *
+ * Entries are not packed.
+ *
+ * Since we have duplicate keys, use a binary search but always follow
+ * all match in the block, not just the first match found.
+ */
+#define        XFS_DA_NODE_MAXDEPTH    5       /* max depth of Btree */
+
+typedef struct xfs_da_node_hdr {
+       struct xfs_da_blkinfo   info;   /* block type, links, etc. */
+       __be16                  __count; /* count of active entries */
+       __be16                  __level; /* level above leaves (leaf == 0) */
+} xfs_da_node_hdr_t;
+
+struct xfs_da3_node_hdr {
+       struct xfs_da3_blkinfo  info;   /* block type, links, etc. */
+       __be16                  __count; /* count of active entries */
+       __be16                  __level; /* level above leaves (leaf == 0) */
+       __be32                  __pad32;
+};
+
+#define XFS_DA3_NODE_CRC_OFF   (offsetof(struct xfs_da3_node_hdr, info.crc))
+
+typedef struct xfs_da_node_entry {
+       __be32  hashval;        /* hash value for this descendant */
+       __be32  before;         /* Btree block before this key */
+} xfs_da_node_entry_t;
+
+typedef struct xfs_da_intnode {
+       struct xfs_da_node_hdr  hdr;
+       struct xfs_da_node_entry __btree[];
+} xfs_da_intnode_t;
+
+struct xfs_da3_intnode {
+       struct xfs_da3_node_hdr hdr;
+       struct xfs_da_node_entry __btree[];
+};
+
+/*
+ * In-core version of the node header to abstract the differences in the v2 and
+ * v3 disk format of the headers. Callers need to convert to/from disk format as
+ * appropriate.
+ */
+struct xfs_da3_icnode_hdr {
+       __uint32_t      forw;
+       __uint32_t      back;
+       __uint16_t      magic;
+       __uint16_t      count;
+       __uint16_t      level;
+};
+
+#define        XFS_LBSIZE(mp)  (mp)->m_sb.sb_blocksize
 
 /*
  * Directory version 2.
@@ -189,79 +294,6 @@ xfs_dir2_sf_firstentry(struct xfs_dir2_sf_hdr *hdr)
                ((char *)hdr + xfs_dir2_sf_hdr_size(hdr->i8count));
 }
 
-static inline int
-xfs_dir3_sf_entsize(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_hdr  *hdr,
-       int                     len)
-{
-       int count = sizeof(struct xfs_dir2_sf_entry);   /* namelen + offset */
-
-       count += len;                                   /* name */
-       count += hdr->i8count ? sizeof(xfs_dir2_ino8_t) :
-                               sizeof(xfs_dir2_ino4_t); /* ino # */
-       if (xfs_sb_version_hasftype(&mp->m_sb))
-               count += sizeof(__uint8_t);             /* file type */
-       return count;
-}
-
-static inline struct xfs_dir2_sf_entry *
-xfs_dir3_sf_nextentry(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_hdr  *hdr,
-       struct xfs_dir2_sf_entry *sfep)
-{
-       return (struct xfs_dir2_sf_entry *)
-               ((char *)sfep + xfs_dir3_sf_entsize(mp, hdr, sfep->namelen));
-}
-
-/*
- * in dir3 shortform directories, the file type field is stored at a variable
- * offset after the inode number. Because it's only a single byte, endian
- * conversion is not necessary.
- */
-static inline __uint8_t *
-xfs_dir3_sfe_ftypep(
-       struct xfs_dir2_sf_hdr  *hdr,
-       struct xfs_dir2_sf_entry *sfep)
-{
-       return (__uint8_t *)&sfep->name[sfep->namelen];
-}
-
-static inline __uint8_t
-xfs_dir3_sfe_get_ftype(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_hdr  *hdr,
-       struct xfs_dir2_sf_entry *sfep)
-{
-       __uint8_t       *ftp;
-
-       if (!xfs_sb_version_hasftype(&mp->m_sb))
-               return XFS_DIR3_FT_UNKNOWN;
-
-       ftp = xfs_dir3_sfe_ftypep(hdr, sfep);
-       if (*ftp >= XFS_DIR3_FT_MAX)
-               return XFS_DIR3_FT_UNKNOWN;
-       return *ftp;
-}
-
-static inline void
-xfs_dir3_sfe_put_ftype(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_hdr  *hdr,
-       struct xfs_dir2_sf_entry *sfep,
-       __uint8_t               ftype)
-{
-       __uint8_t       *ftp;
-
-       ASSERT(ftype < XFS_DIR3_FT_MAX);
-
-       if (!xfs_sb_version_hasftype(&mp->m_sb))
-               return;
-       ftp = xfs_dir3_sfe_ftypep(hdr, sfep);
-       *ftp = ftype;
-}
-
 /*
  * Data block structures.
  *
@@ -345,17 +377,6 @@ struct xfs_dir3_data_hdr {
 
 #define XFS_DIR3_DATA_CRC_OFF  offsetof(struct xfs_dir3_data_hdr, hdr.crc)
 
-static inline struct xfs_dir2_data_free *
-xfs_dir3_data_bestfree_p(struct xfs_dir2_data_hdr *hdr)
-{
-       if (hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-           hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC)) {
-               struct xfs_dir3_data_hdr *hdr3 = (struct xfs_dir3_data_hdr *)hdr;
-               return hdr3->best_free;
-       }
-       return hdr->bestfree;
-}
-
 /*
  * Active entry in a data block.
  *
@@ -388,72 +409,6 @@ typedef struct xfs_dir2_data_unused {
        __be16                  tag;            /* starting offset of us */
 } xfs_dir2_data_unused_t;
 
-/*
- * Size of a data entry.
- */
-static inline int
-__xfs_dir3_data_entsize(
-       bool    ftype,
-       int     n)
-{
-       int     size = offsetof(struct xfs_dir2_data_entry, name[0]);
-
-       size += n;
-       size += sizeof(xfs_dir2_data_off_t);
-       if (ftype)
-               size += sizeof(__uint8_t);
-       return roundup(size, XFS_DIR2_DATA_ALIGN);
-}
-static inline int
-xfs_dir3_data_entsize(
-       struct xfs_mount        *mp,
-       int                     n)
-{
-       bool ftype = xfs_sb_version_hasftype(&mp->m_sb) ? true : false;
-       return __xfs_dir3_data_entsize(ftype, n);
-}
-
-static inline __uint8_t
-xfs_dir3_dirent_get_ftype(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_data_entry *dep)
-{
-       if (xfs_sb_version_hasftype(&mp->m_sb)) {
-               __uint8_t       type = dep->name[dep->namelen];
-
-               ASSERT(type < XFS_DIR3_FT_MAX);
-               if (type < XFS_DIR3_FT_MAX)
-                       return type;
-
-       }
-       return XFS_DIR3_FT_UNKNOWN;
-}
-
-static inline void
-xfs_dir3_dirent_put_ftype(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_data_entry *dep,
-       __uint8_t               type)
-{
-       ASSERT(type < XFS_DIR3_FT_MAX);
-       ASSERT(dep->namelen != 0);
-
-       if (xfs_sb_version_hasftype(&mp->m_sb))
-               dep->name[dep->namelen] = type;
-}
-
-/*
- * Pointer to an entry's tag word.
- */
-static inline __be16 *
-xfs_dir3_data_entry_tag_p(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_data_entry *dep)
-{
-       return (__be16 *)((char *)dep +
-               xfs_dir3_data_entsize(mp, dep->namelen) - sizeof(__be16));
-}
-
 /*
  * Pointer to a freespace's tag word.
  */
@@ -464,93 +419,6 @@ xfs_dir2_data_unused_tag_p(struct xfs_dir2_data_unused *dup)
                        be16_to_cpu(dup->length) - sizeof(__be16));
 }
 
-static inline size_t
-xfs_dir3_data_hdr_size(bool dir3)
-{
-       if (dir3)
-               return sizeof(struct xfs_dir3_data_hdr);
-       return sizeof(struct xfs_dir2_data_hdr);
-}
-
-static inline size_t
-xfs_dir3_data_entry_offset(struct xfs_dir2_data_hdr *hdr)
-{
-       bool dir3 = hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-                   hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
-       return xfs_dir3_data_hdr_size(dir3);
-}
-
-static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_entry_p(struct xfs_dir2_data_hdr *hdr)
-{
-       return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_entry_offset(hdr));
-}
-
-static inline struct xfs_dir2_data_unused *
-xfs_dir3_data_unused_p(struct xfs_dir2_data_hdr *hdr)
-{
-       return (struct xfs_dir2_data_unused *)
-               ((char *)hdr + xfs_dir3_data_entry_offset(hdr));
-}
-
-/*
- * Offsets of . and .. in data space (always block 0)
- *
- * XXX: there is scope for significant optimisation of the logic here. Right
- * now we are checking for "dir3 format" over and over again. Ideally we should
- * only do it once for each operation.
- */
-static inline xfs_dir2_data_aoff_t
-xfs_dir3_data_dot_offset(struct xfs_mount *mp)
-{
-       return xfs_dir3_data_hdr_size(xfs_sb_version_hascrc(&mp->m_sb));
-}
-
-static inline xfs_dir2_data_aoff_t
-xfs_dir3_data_dotdot_offset(struct xfs_mount *mp)
-{
-       return xfs_dir3_data_dot_offset(mp) +
-               xfs_dir3_data_entsize(mp, 1);
-}
-
-static inline xfs_dir2_data_aoff_t
-xfs_dir3_data_first_offset(struct xfs_mount *mp)
-{
-       return xfs_dir3_data_dotdot_offset(mp) +
-               xfs_dir3_data_entsize(mp, 2);
-}
-
-/*
- * location of . and .. in data space (always block 0)
- */
-static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_dot_entry_p(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_data_hdr *hdr)
-{
-       return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_dot_offset(mp));
-}
-
-static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_dotdot_entry_p(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_data_hdr *hdr)
-{
-       return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_dotdot_offset(mp));
-}
-
-static inline struct xfs_dir2_data_entry *
-xfs_dir3_data_first_entry_p(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_data_hdr *hdr)
-{
-       return (struct xfs_dir2_data_entry *)
-               ((char *)hdr + xfs_dir3_data_first_offset(mp));
-}
-
 /*
  * Leaf block structures.
  *
@@ -645,39 +513,6 @@ struct xfs_dir3_leaf {
 
 #define XFS_DIR3_LEAF_CRC_OFF  offsetof(struct xfs_dir3_leaf_hdr, info.crc)
 
-extern void xfs_dir3_leaf_hdr_from_disk(struct xfs_dir3_icleaf_hdr *to,
-                                       struct xfs_dir2_leaf *from);
-
-static inline int
-xfs_dir3_leaf_hdr_size(struct xfs_dir2_leaf *lp)
-{
-       if (lp->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAF1_MAGIC) ||
-           lp->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC))
-               return sizeof(struct xfs_dir3_leaf_hdr);
-       return sizeof(struct xfs_dir2_leaf_hdr);
-}
-
-static inline int
-xfs_dir3_max_leaf_ents(struct xfs_mount *mp, struct xfs_dir2_leaf *lp)
-{
-       return (mp->m_dirblksize - xfs_dir3_leaf_hdr_size(lp)) /
-               (uint)sizeof(struct xfs_dir2_leaf_entry);
-}
-
-/*
- * Get address of the bestcount field in the single-leaf block.
- */
-static inline struct xfs_dir2_leaf_entry *
-xfs_dir3_leaf_ents_p(struct xfs_dir2_leaf *lp)
-{
-       if (lp->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAF1_MAGIC) ||
-           lp->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC)) {
-               struct xfs_dir3_leaf *lp3 = (struct xfs_dir3_leaf *)lp;
-               return lp3->__ents;
-       }
-       return lp->__ents;
-}
-
 /*
  * Get address of the bestcount field in the single-leaf block.
  */
@@ -869,48 +704,6 @@ struct xfs_dir3_icfree_hdr {
 
 };
 
-void xfs_dir3_free_hdr_from_disk(struct xfs_dir3_icfree_hdr *to,
-                                struct xfs_dir2_free *from);
-
-static inline int
-xfs_dir3_free_hdr_size(struct xfs_mount *mp)
-{
-       if (xfs_sb_version_hascrc(&mp->m_sb))
-               return sizeof(struct xfs_dir3_free_hdr);
-       return sizeof(struct xfs_dir2_free_hdr);
-}
-
-static inline int
-xfs_dir3_free_max_bests(struct xfs_mount *mp)
-{
-       return (mp->m_dirblksize - xfs_dir3_free_hdr_size(mp)) /
-               sizeof(xfs_dir2_data_off_t);
-}
-
-static inline __be16 *
-xfs_dir3_free_bests_p(struct xfs_mount *mp, struct xfs_dir2_free *free)
-{
-       return (__be16 *)((char *)free + xfs_dir3_free_hdr_size(mp));
-}
-
-/*
- * Convert data space db to the corresponding free db.
- */
-static inline xfs_dir2_db_t
-xfs_dir2_db_to_fdb(struct xfs_mount *mp, xfs_dir2_db_t db)
-{
-       return XFS_DIR2_FREE_FIRSTDB(mp) + db / xfs_dir3_free_max_bests(mp);
-}
-
-/*
- * Convert data space db to the corresponding index in a free db.
- */
-static inline int
-xfs_dir2_db_to_fdindex(struct xfs_mount *mp, xfs_dir2_db_t db)
-{
-       return db % xfs_dir3_free_max_bests(mp);
-}
-
 /*
  * Single block format.
  *
@@ -961,4 +754,262 @@ xfs_dir2_block_leaf_p(struct xfs_dir2_block_tail *btp)
        return ((struct xfs_dir2_leaf_entry *)btp) - be32_to_cpu(btp->count);
 }
 
-#endif /* __XFS_DIR2_FORMAT_H__ */
+
+/*
+ * Attribute storage layout
+ *
+ * Attribute lists are structured around Btrees where all the data
+ * elements are in the leaf nodes.  Attribute names are hashed into an int,
+ * then that int is used as the index into the Btree.  Since the hashval
+ * of an attribute name may not be unique, we may have duplicate keys.  The
+ * internal links in the Btree are logical block offsets into the file.
+ *
+ *========================================================================
+ * Attribute structure when equal to XFS_LBSIZE(mp) bytes.
+ *========================================================================
+ *
+ * Struct leaf_entry's are packed from the top.  Name/values grow from the
+ * bottom but are not packed.  The freemap contains run-length-encoded entries
+ * for the free bytes after the leaf_entry's, but only the N largest such,
+ * smaller runs are dropped.  When the freemap doesn't show enough space
+ * for an allocation, we compact the name/value area and try again.  If we
+ * still don't have enough space, then we have to split the block.  The
+ * name/value structs (both local and remote versions) must be 32bit aligned.
+ *
+ * Since we have duplicate hash keys, for each key that matches, compare
+ * the actual name string.  The root and intermediate node search always
+ * takes the first-in-the-block key match found, so we should only have
+ * to work "forw"ard.  If none matches, continue with the "forw"ard leaf
+ * nodes until the hash key changes or the attribute name is found.
+ *
+ * We store the fact that an attribute is a ROOT/USER/SECURE attribute in
+ * the leaf_entry.  The namespaces are independent only because we also look
+ * at the namespace bit when we are looking for a matching attribute name.
+ *
+ * We also store an "incomplete" bit in the leaf_entry.  It shows that an
+ * attribute is in the middle of being created and should not be shown to
+ * the user if we crash during the time that the bit is set.  We clear the
+ * bit when we have finished setting up the attribute.  We do this because
+ * we cannot create some large attributes inside a single transaction, and we
+ * need some indication that we weren't finished if we crash in the middle.
+ */
+#define XFS_ATTR_LEAF_MAPSIZE  3       /* how many freespace slots */
+
+typedef struct xfs_attr_leaf_map {     /* RLE map of free bytes */
+       __be16  base;                     /* base of free region */
+       __be16  size;                     /* length of free region */
+} xfs_attr_leaf_map_t;
+
+typedef struct xfs_attr_leaf_hdr {     /* constant-structure header block */
+       xfs_da_blkinfo_t info;          /* block type, links, etc. */
+       __be16  count;                  /* count of active leaf_entry's */
+       __be16  usedbytes;              /* num bytes of names/values stored */
+       __be16  firstused;              /* first used byte in name area */
+       __u8    holes;                  /* != 0 if blk needs compaction */
+       __u8    pad1;
+       xfs_attr_leaf_map_t freemap[XFS_ATTR_LEAF_MAPSIZE];
+                                       /* N largest free regions */
+} xfs_attr_leaf_hdr_t;
+
+typedef struct xfs_attr_leaf_entry {   /* sorted on key, not name */
+       __be32  hashval;                /* hash value of name */
+       __be16  nameidx;                /* index into buffer of name/value */
+       __u8    flags;                  /* LOCAL/ROOT/SECURE/INCOMPLETE flag */
+       __u8    pad2;                   /* unused pad byte */
+} xfs_attr_leaf_entry_t;
+
+typedef struct xfs_attr_leaf_name_local {
+       __be16  valuelen;               /* number of bytes in value */
+       __u8    namelen;                /* length of name bytes */
+       __u8    nameval[1];             /* name/value bytes */
+} xfs_attr_leaf_name_local_t;
+
+typedef struct xfs_attr_leaf_name_remote {
+       __be32  valueblk;               /* block number of value bytes */
+       __be32  valuelen;               /* number of bytes in value */
+       __u8    namelen;                /* length of name bytes */
+       __u8    name[1];                /* name bytes */
+} xfs_attr_leaf_name_remote_t;
+
+typedef struct xfs_attr_leafblock {
+       xfs_attr_leaf_hdr_t     hdr;    /* constant-structure header block */
+       xfs_attr_leaf_entry_t   entries[1];     /* sorted on key, not name */
+       xfs_attr_leaf_name_local_t namelist;    /* grows from bottom of buf */
+       xfs_attr_leaf_name_remote_t valuelist;  /* grows from bottom of buf */
+} xfs_attr_leafblock_t;
+
+/*
+ * CRC enabled leaf structures. Called "version 3" structures to match the
+ * version number of the directory and dablk structures for this feature, and
+ * attr2 is already taken by the variable inode attribute fork size feature.
+ */
+struct xfs_attr3_leaf_hdr {
+       struct xfs_da3_blkinfo  info;
+       __be16                  count;
+       __be16                  usedbytes;
+       __be16                  firstused;
+       __u8                    holes;
+       __u8                    pad1;
+       struct xfs_attr_leaf_map freemap[XFS_ATTR_LEAF_MAPSIZE];
+       __be32                  pad2;           /* 64 bit alignment */
+};
+
+#define XFS_ATTR3_LEAF_CRC_OFF (offsetof(struct xfs_attr3_leaf_hdr, info.crc))
+
+struct xfs_attr3_leafblock {
+       struct xfs_attr3_leaf_hdr       hdr;
+       struct xfs_attr_leaf_entry      entries[1];
+
+       /*
+        * The rest of the block contains the following structures after the
+        * leaf entries, growing from the bottom up. The variables are never
+        * referenced, the locations accessed purely from helper functions.
+        *
+        * struct xfs_attr_leaf_name_local
+        * struct xfs_attr_leaf_name_remote
+        */
+};
+
+/*
+ * incore, neutral version of the attribute leaf header
+ */
+struct xfs_attr3_icleaf_hdr {
+       __uint32_t      forw;
+       __uint32_t      back;
+       __uint16_t      magic;
+       __uint16_t      count;
+       __uint16_t      usedbytes;
+       __uint16_t      firstused;
+       __u8            holes;
+       struct {
+               __uint16_t      base;
+               __uint16_t      size;
+       } freemap[XFS_ATTR_LEAF_MAPSIZE];
+};
+
+/*
+ * Flags used in the leaf_entry[i].flags field.
+ * NOTE: the INCOMPLETE bit must not collide with the flags bits specified
+ * on the system call, they are "or"ed together for various operations.
+ */
+#define        XFS_ATTR_LOCAL_BIT      0       /* attr is stored locally */
+#define        XFS_ATTR_ROOT_BIT       1       /* limit access to trusted attrs */
+#define        XFS_ATTR_SECURE_BIT     2       /* limit access to secure attrs */
+#define        XFS_ATTR_INCOMPLETE_BIT 7       /* attr in middle of create/delete */
+#define XFS_ATTR_LOCAL         (1 << XFS_ATTR_LOCAL_BIT)
+#define XFS_ATTR_ROOT          (1 << XFS_ATTR_ROOT_BIT)
+#define XFS_ATTR_SECURE                (1 << XFS_ATTR_SECURE_BIT)
+#define XFS_ATTR_INCOMPLETE    (1 << XFS_ATTR_INCOMPLETE_BIT)
+
+/*
+ * Conversion macros for converting namespace bits from argument flags
+ * to ondisk flags.
+ */
+#define XFS_ATTR_NSP_ARGS_MASK         (ATTR_ROOT | ATTR_SECURE)
+#define XFS_ATTR_NSP_ONDISK_MASK       (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_NSP_ONDISK(flags)     ((flags) & XFS_ATTR_NSP_ONDISK_MASK)
+#define XFS_ATTR_NSP_ARGS(flags)       ((flags) & XFS_ATTR_NSP_ARGS_MASK)
+#define XFS_ATTR_NSP_ARGS_TO_ONDISK(x) (((x) & ATTR_ROOT ? XFS_ATTR_ROOT : 0) |\
+                                        ((x) & ATTR_SECURE ? XFS_ATTR_SECURE : 0))
+#define XFS_ATTR_NSP_ONDISK_TO_ARGS(x) (((x) & XFS_ATTR_ROOT ? ATTR_ROOT : 0) |\
+                                        ((x) & XFS_ATTR_SECURE ? ATTR_SECURE : 0))
+
+/*
+ * Alignment for namelist and valuelist entries (since they are mixed
+ * there can be only one alignment value)
+ */
+#define        XFS_ATTR_LEAF_NAME_ALIGN        ((uint)sizeof(xfs_dablk_t))
+
+static inline int
+xfs_attr3_leaf_hdr_size(struct xfs_attr_leafblock *leafp)
+{
+       if (leafp->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC))
+               return sizeof(struct xfs_attr3_leaf_hdr);
+       return sizeof(struct xfs_attr_leaf_hdr);
+}
+
+static inline struct xfs_attr_leaf_entry *
+xfs_attr3_leaf_entryp(xfs_attr_leafblock_t *leafp)
+{
+       if (leafp->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC))
+               return &((struct xfs_attr3_leafblock *)leafp)->entries[0];
+       return &leafp->entries[0];
+}
+
+/*
+ * Cast typed pointers for "local" and "remote" name/value structs.
+ */
+static inline char *
+xfs_attr3_leaf_name(xfs_attr_leafblock_t *leafp, int idx)
+{
+       struct xfs_attr_leaf_entry *entries = xfs_attr3_leaf_entryp(leafp);
+
+       return &((char *)leafp)[be16_to_cpu(entries[idx].nameidx)];
+}
+
+static inline xfs_attr_leaf_name_remote_t *
+xfs_attr3_leaf_name_remote(xfs_attr_leafblock_t *leafp, int idx)
+{
+       return (xfs_attr_leaf_name_remote_t *)xfs_attr3_leaf_name(leafp, idx);
+}
+
+static inline xfs_attr_leaf_name_local_t *
+xfs_attr3_leaf_name_local(xfs_attr_leafblock_t *leafp, int idx)
+{
+       return (xfs_attr_leaf_name_local_t *)xfs_attr3_leaf_name(leafp, idx);
+}
+
+/*
+ * Calculate total bytes used (including trailing pad for alignment) for
+ * a "local" name/value structure, a "remote" name/value structure, and
+ * a pointer which might be either.
+ */
+static inline int xfs_attr_leaf_entsize_remote(int nlen)
+{
+       return ((uint)sizeof(xfs_attr_leaf_name_remote_t) - 1 + (nlen) + \
+               XFS_ATTR_LEAF_NAME_ALIGN - 1) & ~(XFS_ATTR_LEAF_NAME_ALIGN - 1);
+}
+
+static inline int xfs_attr_leaf_entsize_local(int nlen, int vlen)
+{
+       return ((uint)sizeof(xfs_attr_leaf_name_local_t) - 1 + (nlen) + (vlen) +
+               XFS_ATTR_LEAF_NAME_ALIGN - 1) & ~(XFS_ATTR_LEAF_NAME_ALIGN - 1);
+}
+
+static inline int xfs_attr_leaf_entsize_local_max(int bsize)
+{
+       return (((bsize) >> 1) + ((bsize) >> 2));
+}
+
+
+
+/*
+ * Remote attribute block format definition
+ *
+ * There is one of these headers per filesystem block in a remote attribute.
+ * This is done to ensure there is a 1:1 mapping between the attribute value
+ * length and the number of blocks needed to store the attribute. This makes the
+ * verification of a buffer a little more complex, but greatly simplifies the
+ * allocation, reading and writing of these attributes as we don't have to guess
+ * the number of blocks needed to store the attribute data.
+ */
+#define XFS_ATTR3_RMT_MAGIC    0x5841524d      /* XARM */
+
+struct xfs_attr3_rmt_hdr {
+       __be32  rm_magic;
+       __be32  rm_offset;
+       __be32  rm_bytes;
+       __be32  rm_crc;
+       uuid_t  rm_uuid;
+       __be64  rm_owner;
+       __be64  rm_blkno;
+       __be64  rm_lsn;
+};
+
+#define XFS_ATTR3_RMT_CRC_OFF  offsetof(struct xfs_attr3_rmt_hdr, rm_crc)
+
+#define XFS_ATTR3_RMT_BUF_SPACE(mp, bufsize)   \
+       ((bufsize) - (xfs_sb_version_hascrc(&(mp)->m_sb) ? \
+                       sizeof(struct xfs_attr3_rmt_hdr) : 0))
+
+#endif /* __XFS_DA_FORMAT_H__ */
index edf203ab50afa734a74faaace8e626721e7fc1bb..ce16ef02997a9b27e77293033f84366919fe0e48 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_dinode.h"
 
 struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };
 
@@ -96,13 +95,17 @@ xfs_dir_mount(
        ASSERT(xfs_sb_version_hasdirv2(&mp->m_sb));
        ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <=
               XFS_MAX_BLOCKSIZE);
+
+       mp->m_dir_inode_ops = xfs_dir_get_ops(mp, NULL);
+       mp->m_nondir_inode_ops = xfs_nondir_get_ops(mp, NULL);
+
        mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog);
        mp->m_dirblkfsbs = 1 << mp->m_sb.sb_dirblklog;
        mp->m_dirdatablk = xfs_dir2_db_to_da(mp, XFS_DIR2_DATA_FIRSTDB(mp));
        mp->m_dirleafblk = xfs_dir2_db_to_da(mp, XFS_DIR2_LEAF_FIRSTDB(mp));
        mp->m_dirfreeblk = xfs_dir2_db_to_da(mp, XFS_DIR2_FREE_FIRSTDB(mp));
 
-       nodehdr_size = __xfs_da3_node_hdr_size(xfs_sb_version_hascrc(&mp->m_sb));
+       nodehdr_size = mp->m_dir_inode_ops->node_hdr_size;
        mp->m_attr_node_ents = (mp->m_sb.sb_blocksize - nodehdr_size) /
                                (uint)sizeof(xfs_da_node_entry_t);
        mp->m_dir_node_ents = (mp->m_dirblksize - nodehdr_size) /
@@ -113,6 +116,7 @@ xfs_dir_mount(
                mp->m_dirnameops = &xfs_ascii_ci_nameops;
        else
                mp->m_dirnameops = &xfs_default_nameops;
+
 }
 
 /*
index 9910401327d45c788586b2e0953309b81c8c7c6d..cec70e0781ab664f9238c7601d252f9fc339fa37 100644 (file)
@@ -31,6 +31,83 @@ struct xfs_dir2_data_unused;
 
 extern struct xfs_name xfs_name_dotdot;
 
+/*
+ * directory operations vector for encode/decode routines
+ */
+struct xfs_dir_ops {
+       int     (*sf_entsize)(struct xfs_dir2_sf_hdr *hdr, int len);
+       struct xfs_dir2_sf_entry *
+               (*sf_nextentry)(struct xfs_dir2_sf_hdr *hdr,
+                               struct xfs_dir2_sf_entry *sfep);
+       __uint8_t (*sf_get_ftype)(struct xfs_dir2_sf_entry *sfep);
+       void    (*sf_put_ftype)(struct xfs_dir2_sf_entry *sfep,
+                               __uint8_t ftype);
+       xfs_ino_t (*sf_get_ino)(struct xfs_dir2_sf_hdr *hdr,
+                               struct xfs_dir2_sf_entry *sfep);
+       void    (*sf_put_ino)(struct xfs_dir2_sf_hdr *hdr,
+                             struct xfs_dir2_sf_entry *sfep,
+                             xfs_ino_t ino);
+       xfs_ino_t (*sf_get_parent_ino)(struct xfs_dir2_sf_hdr *hdr);
+       void    (*sf_put_parent_ino)(struct xfs_dir2_sf_hdr *hdr,
+                                    xfs_ino_t ino);
+
+       int     (*data_entsize)(int len);
+       __uint8_t (*data_get_ftype)(struct xfs_dir2_data_entry *dep);
+       void    (*data_put_ftype)(struct xfs_dir2_data_entry *dep,
+                               __uint8_t ftype);
+       __be16 * (*data_entry_tag_p)(struct xfs_dir2_data_entry *dep);
+       struct xfs_dir2_data_free *
+               (*data_bestfree_p)(struct xfs_dir2_data_hdr *hdr);
+
+       xfs_dir2_data_aoff_t data_dot_offset;
+       xfs_dir2_data_aoff_t data_dotdot_offset;
+       xfs_dir2_data_aoff_t data_first_offset;
+       size_t  data_entry_offset;
+
+       struct xfs_dir2_data_entry *
+               (*data_dot_entry_p)(struct xfs_dir2_data_hdr *hdr);
+       struct xfs_dir2_data_entry *
+               (*data_dotdot_entry_p)(struct xfs_dir2_data_hdr *hdr);
+       struct xfs_dir2_data_entry *
+               (*data_first_entry_p)(struct xfs_dir2_data_hdr *hdr);
+       struct xfs_dir2_data_entry *
+               (*data_entry_p)(struct xfs_dir2_data_hdr *hdr);
+       struct xfs_dir2_data_unused *
+               (*data_unused_p)(struct xfs_dir2_data_hdr *hdr);
+
+       int     leaf_hdr_size;
+       void    (*leaf_hdr_to_disk)(struct xfs_dir2_leaf *to,
+                                   struct xfs_dir3_icleaf_hdr *from);
+       void    (*leaf_hdr_from_disk)(struct xfs_dir3_icleaf_hdr *to,
+                                     struct xfs_dir2_leaf *from);
+       int     (*leaf_max_ents)(struct xfs_mount *mp);
+       struct xfs_dir2_leaf_entry *
+               (*leaf_ents_p)(struct xfs_dir2_leaf *lp);
+
+       int     node_hdr_size;
+       void    (*node_hdr_to_disk)(struct xfs_da_intnode *to,
+                                   struct xfs_da3_icnode_hdr *from);
+       void    (*node_hdr_from_disk)(struct xfs_da3_icnode_hdr *to,
+                                     struct xfs_da_intnode *from);
+       struct xfs_da_node_entry *
+               (*node_tree_p)(struct xfs_da_intnode *dap);
+
+       int     free_hdr_size;
+       void    (*free_hdr_to_disk)(struct xfs_dir2_free *to,
+                                   struct xfs_dir3_icfree_hdr *from);
+       void    (*free_hdr_from_disk)(struct xfs_dir3_icfree_hdr *to,
+                                     struct xfs_dir2_free *from);
+       int     (*free_max_bests)(struct xfs_mount *mp);
+       __be16 * (*free_bests_p)(struct xfs_dir2_free *free);
+       xfs_dir2_db_t (*db_to_fdb)(struct xfs_mount *mp, xfs_dir2_db_t db);
+       int     (*db_to_fdindex)(struct xfs_mount *mp, xfs_dir2_db_t db);
+};
+
+extern const struct xfs_dir_ops *
+       xfs_dir_get_ops(struct xfs_mount *mp, struct xfs_inode *dp);
+extern const struct xfs_dir_ops *
+       xfs_nondir_get_ops(struct xfs_mount *mp, struct xfs_inode *dp);
+
 /*
  * Generic directory interface routines
  */
@@ -65,37 +142,30 @@ extern int xfs_dir2_sf_to_block(struct xfs_da_args *args);
 /*
  * Interface routines used by userspace utilities
  */
-extern xfs_ino_t xfs_dir2_sf_get_parent_ino(struct xfs_dir2_sf_hdr *sfp);
-extern void xfs_dir2_sf_put_parent_ino(struct xfs_dir2_sf_hdr *sfp,
-               xfs_ino_t ino);
-extern xfs_ino_t xfs_dir3_sfe_get_ino(struct xfs_mount *mp,
-               struct xfs_dir2_sf_hdr *sfp, struct xfs_dir2_sf_entry *sfep);
-extern void xfs_dir3_sfe_put_ino(struct xfs_mount *mp,
-               struct xfs_dir2_sf_hdr *hdr, struct xfs_dir2_sf_entry *sfep,
-               xfs_ino_t ino);
-
 extern int xfs_dir2_isblock(struct xfs_trans *tp, struct xfs_inode *dp, int *r);
 extern int xfs_dir2_isleaf(struct xfs_trans *tp, struct xfs_inode *dp, int *r);
 extern int xfs_dir2_shrink_inode(struct xfs_da_args *args, xfs_dir2_db_t db,
                                struct xfs_buf *bp);
 
-extern void xfs_dir2_data_freescan(struct xfs_mount *mp,
+extern void xfs_dir2_data_freescan(struct xfs_inode *dp,
                struct xfs_dir2_data_hdr *hdr, int *loghead);
-extern void xfs_dir2_data_log_entry(struct xfs_trans *tp, struct xfs_buf *bp,
-               struct xfs_dir2_data_entry *dep);
-extern void xfs_dir2_data_log_header(struct xfs_trans *tp,
+extern void xfs_dir2_data_log_entry(struct xfs_trans *tp, struct xfs_inode *dp,
+               struct xfs_buf *bp, struct xfs_dir2_data_entry *dep);
+extern void xfs_dir2_data_log_header(struct xfs_trans *tp, struct xfs_inode *dp,
                struct xfs_buf *bp);
 extern void xfs_dir2_data_log_unused(struct xfs_trans *tp, struct xfs_buf *bp,
                struct xfs_dir2_data_unused *dup);
-extern void xfs_dir2_data_make_free(struct xfs_trans *tp, struct xfs_buf *bp,
+extern void xfs_dir2_data_make_free(struct xfs_trans *tp, struct xfs_inode *dp,
+               struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
+               xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
+extern void xfs_dir2_data_use_free(struct xfs_trans *tp, struct xfs_inode *dp,
+               struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
                xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
                int *needlogp, int *needscanp);
-extern void xfs_dir2_data_use_free(struct xfs_trans *tp, struct xfs_buf *bp,
-               struct xfs_dir2_data_unused *dup, xfs_dir2_data_aoff_t offset,
-               xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
 
 extern struct xfs_dir2_data_free *xfs_dir2_data_freefind(
-               struct xfs_dir2_data_hdr *hdr, struct xfs_dir2_data_unused *dup);
+               struct xfs_dir2_data_hdr *hdr, struct xfs_dir2_data_free *bf,
+               struct xfs_dir2_data_unused *dup);
 
 extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
 extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
index 12dad188939df2e29c4420574bd20bfb319aae35..90cdbf4b5f1902f983f504b6b704cd2a433ddfbb 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
 #include "xfs_buf_item.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
+#include "xfs_dinode.h"
 
 /*
  * Local function prototypes.
@@ -168,6 +168,7 @@ xfs_dir3_block_init(
 
 static void
 xfs_dir2_block_need_space(
+       struct xfs_inode                *dp,
        struct xfs_dir2_data_hdr        *hdr,
        struct xfs_dir2_block_tail      *btp,
        struct xfs_dir2_leaf_entry      *blp,
@@ -183,7 +184,7 @@ xfs_dir2_block_need_space(
        struct xfs_dir2_data_unused     *enddup = NULL;
 
        *compact = 0;
-       bf = xfs_dir3_data_bestfree_p(hdr);
+       bf = dp->d_ops->data_bestfree_p(hdr);
 
        /*
         * If there are stale entries we'll use one for the leaf.
@@ -280,6 +281,7 @@ out:
 static void
 xfs_dir2_block_compact(
        struct xfs_trans                *tp,
+       struct xfs_inode                *dp,
        struct xfs_buf                  *bp,
        struct xfs_dir2_data_hdr        *hdr,
        struct xfs_dir2_block_tail      *btp,
@@ -312,7 +314,7 @@ xfs_dir2_block_compact(
        *lfloglow = toidx + 1 - (be32_to_cpu(btp->stale) - 1);
        *lfloghigh -= be32_to_cpu(btp->stale) - 1;
        be32_add_cpu(&btp->count, -(be32_to_cpu(btp->stale) - 1));
-       xfs_dir2_data_make_free(tp, bp,
+       xfs_dir2_data_make_free(tp, dp, bp,
                (xfs_dir2_data_aoff_t)((char *)blp - (char *)hdr),
                (xfs_dir2_data_aoff_t)((be32_to_cpu(btp->stale) - 1) * sizeof(*blp)),
                needlog, &needscan);
@@ -323,7 +325,7 @@ xfs_dir2_block_compact(
         * This needs to happen before the next call to use_free.
         */
        if (needscan)
-               xfs_dir2_data_freescan(tp->t_mountp, hdr, needlog);
+               xfs_dir2_data_freescan(dp, hdr, needlog);
 }
 
 /*
@@ -369,7 +371,7 @@ xfs_dir2_block_addname(
        if (error)
                return error;
 
-       len = xfs_dir3_data_entsize(mp, args->namelen);
+       len = dp->d_ops->data_entsize(args->namelen);
 
        /*
         * Set up pointers to parts of the block.
@@ -382,7 +384,7 @@ xfs_dir2_block_addname(
         * Find out if we can reuse stale entries or whether we need extra
         * space for entry and new leaf.
         */
-       xfs_dir2_block_need_space(hdr, btp, blp, &tagp, &dup,
+       xfs_dir2_block_need_space(dp, hdr, btp, blp, &tagp, &dup,
                                  &enddup, &compact, len);
 
        /*
@@ -418,7 +420,7 @@ xfs_dir2_block_addname(
         * If need to compact the leaf entries, do it now.
         */
        if (compact) {
-               xfs_dir2_block_compact(tp, bp, hdr, btp, blp, &needlog,
+               xfs_dir2_block_compact(tp, dp, bp, hdr, btp, blp, &needlog,
                                      &lfloghigh, &lfloglow);
                /* recalculate blp post-compaction */
                blp = xfs_dir2_block_leaf_p(btp);
@@ -453,7 +455,7 @@ xfs_dir2_block_addname(
                /*
                 * Mark the space needed for the new leaf entry, now in use.
                 */
-               xfs_dir2_data_use_free(tp, bp, enddup,
+               xfs_dir2_data_use_free(tp, dp, bp, enddup,
                        (xfs_dir2_data_aoff_t)
                        ((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
                         sizeof(*blp)),
@@ -468,7 +470,7 @@ xfs_dir2_block_addname(
                 * This needs to happen before the next call to use_free.
                 */
                if (needscan) {
-                       xfs_dir2_data_freescan(mp, hdr, &needlog);
+                       xfs_dir2_data_freescan(dp, hdr, &needlog);
                        needscan = 0;
                }
                /*
@@ -540,7 +542,7 @@ xfs_dir2_block_addname(
        /*
         * Mark space for the data entry used.
         */
-       xfs_dir2_data_use_free(tp, bp, dup,
+       xfs_dir2_data_use_free(tp, dp, bp, dup,
                (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
                (xfs_dir2_data_aoff_t)len, &needlog, &needscan);
        /*
@@ -549,18 +551,18 @@ xfs_dir2_block_addname(
        dep->inumber = cpu_to_be64(args->inumber);
        dep->namelen = args->namelen;
        memcpy(dep->name, args->name, args->namelen);
-       xfs_dir3_dirent_put_ftype(mp, dep, args->filetype);
-       tagp = xfs_dir3_data_entry_tag_p(mp, dep);
+       dp->d_ops->data_put_ftype(dep, args->filetype);
+       tagp = dp->d_ops->data_entry_tag_p(dep);
        *tagp = cpu_to_be16((char *)dep - (char *)hdr);
        /*
         * Clean up the bestfree array and log the header, tail, and entry.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        if (needlog)
-               xfs_dir2_data_log_header(tp, bp);
+               xfs_dir2_data_log_header(tp, dp, bp);
        xfs_dir2_block_log_tail(tp, bp);
-       xfs_dir2_data_log_entry(tp, bp, dep);
+       xfs_dir2_data_log_entry(tp, dp, bp, dep);
        xfs_dir3_data_check(dp, bp);
        return 0;
 }
@@ -642,7 +644,7 @@ xfs_dir2_block_lookup(
         * Fill in inode number, CI name if appropriate, release the block.
         */
        args->inumber = be64_to_cpu(dep->inumber);
-       args->filetype = xfs_dir3_dirent_get_ftype(mp, dep);
+       args->filetype = dp->d_ops->data_get_ftype(dep);
        error = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
        xfs_trans_brelse(args->trans, bp);
        return XFS_ERROR(error);
@@ -799,9 +801,9 @@ xfs_dir2_block_removename(
         * Mark the data entry's space free.
         */
        needlog = needscan = 0;
-       xfs_dir2_data_make_free(tp, bp,
+       xfs_dir2_data_make_free(tp, dp, bp,
                (xfs_dir2_data_aoff_t)((char *)dep - (char *)hdr),
-               xfs_dir3_data_entsize(mp, dep->namelen), &needlog, &needscan);
+               dp->d_ops->data_entsize(dep->namelen), &needlog, &needscan);
        /*
         * Fix up the block tail.
         */
@@ -816,9 +818,9 @@ xfs_dir2_block_removename(
         * Fix up bestfree, log the header if necessary.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        if (needlog)
-               xfs_dir2_data_log_header(tp, bp);
+               xfs_dir2_data_log_header(tp, dp, bp);
        xfs_dir3_data_check(dp, bp);
        /*
         * See if the size as a shortform is good enough.
@@ -875,8 +877,8 @@ xfs_dir2_block_replace(
         * Change the inode number to the new value.
         */
        dep->inumber = cpu_to_be64(args->inumber);
-       xfs_dir3_dirent_put_ftype(mp, dep, args->filetype);
-       xfs_dir2_data_log_entry(args->trans, bp, dep);
+       dp->d_ops->data_put_ftype(dep, args->filetype);
+       xfs_dir2_data_log_entry(args->trans, dp, bp, dep);
        xfs_dir3_data_check(dp, bp);
        return 0;
 }
@@ -934,8 +936,8 @@ xfs_dir2_leaf_to_block(
        tp = args->trans;
        mp = dp->i_mount;
        leaf = lbp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
 
        ASSERT(leafhdr.magic == XFS_DIR2_LEAF1_MAGIC ||
@@ -949,7 +951,7 @@ xfs_dir2_leaf_to_block(
        while (dp->i_d.di_size > mp->m_dirblksize) {
                int hdrsz;
 
-               hdrsz = xfs_dir3_data_hdr_size(xfs_sb_version_hascrc(&mp->m_sb));
+               hdrsz = dp->d_ops->data_entry_offset;
                bestsp = xfs_dir2_leaf_bests_p(ltp);
                if (be16_to_cpu(bestsp[be32_to_cpu(ltp->bestcount) - 1]) ==
                                            mp->m_dirblksize - hdrsz) {
@@ -999,7 +1001,7 @@ xfs_dir2_leaf_to_block(
        /*
         * Use up the space at the end of the block (blp/btp).
         */
-       xfs_dir2_data_use_free(tp, dbp, dup, mp->m_dirblksize - size, size,
+       xfs_dir2_data_use_free(tp, dp, dbp, dup, mp->m_dirblksize - size, size,
                &needlog, &needscan);
        /*
         * Initialize the block tail.
@@ -1023,9 +1025,9 @@ xfs_dir2_leaf_to_block(
         * Scan the bestfree if we need it and log the data block header.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        if (needlog)
-               xfs_dir2_data_log_header(tp, dbp);
+               xfs_dir2_data_log_header(tp, dp, dbp);
        /*
         * Pitch the old leaf block.
         */
@@ -1136,9 +1138,9 @@ xfs_dir2_sf_to_block(
         * The whole thing is initialized to free by the init routine.
         * Say we're using the leaf and tail area.
         */
-       dup = xfs_dir3_data_unused_p(hdr);
+       dup = dp->d_ops->data_unused_p(hdr);
        needlog = needscan = 0;
-       xfs_dir2_data_use_free(tp, bp, dup, mp->m_dirblksize - i, i, &needlog,
+       xfs_dir2_data_use_free(tp, dp, bp, dup, mp->m_dirblksize - i, i, &needlog,
                &needscan);
        ASSERT(needscan == 0);
        /*
@@ -1152,38 +1154,38 @@ xfs_dir2_sf_to_block(
        /*
         * Remove the freespace, we'll manage it.
         */
-       xfs_dir2_data_use_free(tp, bp, dup,
+       xfs_dir2_data_use_free(tp, dp, bp, dup,
                (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
                be16_to_cpu(dup->length), &needlog, &needscan);
        /*
         * Create entry for .
         */
-       dep = xfs_dir3_data_dot_entry_p(mp, hdr);
+       dep = dp->d_ops->data_dot_entry_p(hdr);
        dep->inumber = cpu_to_be64(dp->i_ino);
        dep->namelen = 1;
        dep->name[0] = '.';
-       xfs_dir3_dirent_put_ftype(mp, dep, XFS_DIR3_FT_DIR);
-       tagp = xfs_dir3_data_entry_tag_p(mp, dep);
+       dp->d_ops->data_put_ftype(dep, XFS_DIR3_FT_DIR);
+       tagp = dp->d_ops->data_entry_tag_p(dep);
        *tagp = cpu_to_be16((char *)dep - (char *)hdr);
-       xfs_dir2_data_log_entry(tp, bp, dep);
+       xfs_dir2_data_log_entry(tp, dp, bp, dep);
        blp[0].hashval = cpu_to_be32(xfs_dir_hash_dot);
        blp[0].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
                                (char *)dep - (char *)hdr));
        /*
         * Create entry for ..
         */
-       dep = xfs_dir3_data_dotdot_entry_p(mp, hdr);
-       dep->inumber = cpu_to_be64(xfs_dir2_sf_get_parent_ino(sfp));
+       dep = dp->d_ops->data_dotdot_entry_p(hdr);
+       dep->inumber = cpu_to_be64(dp->d_ops->sf_get_parent_ino(sfp));
        dep->namelen = 2;
        dep->name[0] = dep->name[1] = '.';
-       xfs_dir3_dirent_put_ftype(mp, dep, XFS_DIR3_FT_DIR);
-       tagp = xfs_dir3_data_entry_tag_p(mp, dep);
+       dp->d_ops->data_put_ftype(dep, XFS_DIR3_FT_DIR);
+       tagp = dp->d_ops->data_entry_tag_p(dep);
        *tagp = cpu_to_be16((char *)dep - (char *)hdr);
-       xfs_dir2_data_log_entry(tp, bp, dep);
+       xfs_dir2_data_log_entry(tp, dp, bp, dep);
        blp[1].hashval = cpu_to_be32(xfs_dir_hash_dotdot);
        blp[1].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
                                (char *)dep - (char *)hdr));
-       offset = xfs_dir3_data_first_offset(mp);
+       offset = dp->d_ops->data_first_offset;
        /*
         * Loop over existing entries, stuff them in.
         */
@@ -1214,7 +1216,9 @@ xfs_dir2_sf_to_block(
                        *xfs_dir2_data_unused_tag_p(dup) = cpu_to_be16(
                                ((char *)dup - (char *)hdr));
                        xfs_dir2_data_log_unused(tp, bp, dup);
-                       xfs_dir2_data_freeinsert(hdr, dup, &dummy);
+                       xfs_dir2_data_freeinsert(hdr,
+                                                dp->d_ops->data_bestfree_p(hdr),
+                                                dup, &dummy);
                        offset += be16_to_cpu(dup->length);
                        continue;
                }
@@ -1222,14 +1226,13 @@ xfs_dir2_sf_to_block(
                 * Copy a real entry.
                 */
                dep = (xfs_dir2_data_entry_t *)((char *)hdr + newoffset);
-               dep->inumber = cpu_to_be64(xfs_dir3_sfe_get_ino(mp, sfp, sfep));
+               dep->inumber = cpu_to_be64(dp->d_ops->sf_get_ino(sfp, sfep));
                dep->namelen = sfep->namelen;
-               xfs_dir3_dirent_put_ftype(mp, dep,
-                                       xfs_dir3_sfe_get_ftype(mp, sfp, sfep));
+               dp->d_ops->data_put_ftype(dep, dp->d_ops->sf_get_ftype(sfep));
                memcpy(dep->name, sfep->name, dep->namelen);
-               tagp = xfs_dir3_data_entry_tag_p(mp, dep);
+               tagp = dp->d_ops->data_entry_tag_p(dep);
                *tagp = cpu_to_be16((char *)dep - (char *)hdr);
-               xfs_dir2_data_log_entry(tp, bp, dep);
+               xfs_dir2_data_log_entry(tp, dp, bp, dep);
                name.name = sfep->name;
                name.len = sfep->namelen;
                blp[2 + i].hashval = cpu_to_be32(mp->m_dirnameops->
@@ -1240,7 +1243,7 @@ xfs_dir2_sf_to_block(
                if (++i == sfp->count)
                        sfep = NULL;
                else
-                       sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
+                       sfep = dp->d_ops->sf_nextentry(sfp, sfep);
        }
        /* Done with the temporary buffer */
        kmem_free(sfp);
index 47e1326c169a08c71d8d21ac51e8d113444d3295..70acff4ee1739860a4ed8a311aa8ff2f2b33aafe 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_cksum.h"
 
@@ -63,11 +62,18 @@ __xfs_dir3_data_check(
        char                    *p;             /* current data position */
        int                     stale;          /* count of stale leaves */
        struct xfs_name         name;
+       const struct xfs_dir_ops *ops;
 
        mp = bp->b_target->bt_mount;
+
+       /*
+        * We can be passed a null dp here from a verifier, so we need to go the
+        * hard way to get them.
+        */
+       ops = xfs_dir_get_ops(mp, dp);
+
        hdr = bp->b_addr;
-       bf = xfs_dir3_data_bestfree_p(hdr);
-       p = (char *)xfs_dir3_data_entry_p(hdr);
+       p = (char *)ops->data_entry_p(hdr);
 
        switch (hdr->magic) {
        case cpu_to_be32(XFS_DIR3_BLOCK_MAGIC):
@@ -75,6 +81,16 @@ __xfs_dir3_data_check(
                btp = xfs_dir2_block_tail_p(mp, hdr);
                lep = xfs_dir2_block_leaf_p(btp);
                endp = (char *)lep;
+
+               /*
+                * The number of leaf entries is limited by the size of the
+                * block and the amount of space used by the data entries.
+                * We don't know how much space is used by the data entries yet,
+                * so just ensure that the count falls somewhere inside the
+                * block right now.
+                */
+               XFS_WANT_CORRUPTED_RETURN(be32_to_cpu(btp->count) <
+                       ((char *)btp - p) / sizeof(struct xfs_dir2_leaf_entry));
                break;
        case cpu_to_be32(XFS_DIR3_DATA_MAGIC):
        case cpu_to_be32(XFS_DIR2_DATA_MAGIC):
@@ -85,10 +101,11 @@ __xfs_dir3_data_check(
                return EFSCORRUPTED;
        }
 
-       count = lastfree = freeseen = 0;
        /*
         * Account for zero bestfree entries.
         */
+       bf = ops->data_bestfree_p(hdr);
+       count = lastfree = freeseen = 0;
        if (!bf[0].length) {
                XFS_WANT_CORRUPTED_RETURN(!bf[0].offset);
                freeseen |= 1 << 0;
@@ -121,7 +138,7 @@ __xfs_dir3_data_check(
                        XFS_WANT_CORRUPTED_RETURN(
                                be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) ==
                                               (char *)dup - (char *)hdr);
-                       dfp = xfs_dir2_data_freefind(hdr, dup);
+                       dfp = xfs_dir2_data_freefind(hdr, bf, dup);
                        if (dfp) {
                                i = (int)(dfp - bf);
                                XFS_WANT_CORRUPTED_RETURN(
@@ -147,10 +164,10 @@ __xfs_dir3_data_check(
                XFS_WANT_CORRUPTED_RETURN(
                        !xfs_dir_ino_validate(mp, be64_to_cpu(dep->inumber)));
                XFS_WANT_CORRUPTED_RETURN(
-                       be16_to_cpu(*xfs_dir3_data_entry_tag_p(mp, dep)) ==
+                       be16_to_cpu(*ops->data_entry_tag_p(dep)) ==
                                               (char *)dep - (char *)hdr);
                XFS_WANT_CORRUPTED_RETURN(
-                       xfs_dir3_dirent_get_ftype(mp, dep) < XFS_DIR3_FT_MAX);
+                               ops->data_get_ftype(dep) < XFS_DIR3_FT_MAX);
                count++;
                lastfree = 0;
                if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
@@ -168,7 +185,7 @@ __xfs_dir3_data_check(
                        }
                        XFS_WANT_CORRUPTED_RETURN(i < be32_to_cpu(btp->count));
                }
-               p += xfs_dir3_data_entsize(mp, dep->namelen);
+               p += ops->data_entsize(dep->namelen);
        }
        /*
         * Need to have seen all the entries and all the bestfree slots.
@@ -327,19 +344,18 @@ xfs_dir3_data_readahead(
  */
 xfs_dir2_data_free_t *
 xfs_dir2_data_freefind(
-       xfs_dir2_data_hdr_t     *hdr,           /* data block */
-       xfs_dir2_data_unused_t  *dup)           /* data unused entry */
+       struct xfs_dir2_data_hdr *hdr,          /* data block header */
+       struct xfs_dir2_data_free *bf,          /* bestfree table pointer */
+       struct xfs_dir2_data_unused *dup)       /* unused space */
 {
        xfs_dir2_data_free_t    *dfp;           /* bestfree entry */
        xfs_dir2_data_aoff_t    off;            /* offset value needed */
-       struct xfs_dir2_data_free *bf;
 #ifdef DEBUG
        int                     matched;        /* matched the value */
        int                     seenzero;       /* saw a 0 bestfree entry */
 #endif
 
        off = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
-       bf = xfs_dir3_data_bestfree_p(hdr);
 
 #ifdef DEBUG
        /*
@@ -399,11 +415,11 @@ xfs_dir2_data_freefind(
  */
 xfs_dir2_data_free_t *                         /* entry inserted */
 xfs_dir2_data_freeinsert(
-       xfs_dir2_data_hdr_t     *hdr,           /* data block pointer */
-       xfs_dir2_data_unused_t  *dup,           /* unused space */
+       struct xfs_dir2_data_hdr *hdr,          /* data block pointer */
+       struct xfs_dir2_data_free *dfp,         /* bestfree table pointer */
+       struct xfs_dir2_data_unused *dup,       /* unused space */
        int                     *loghead)       /* log the data header (out) */
 {
-       xfs_dir2_data_free_t    *dfp;           /* bestfree table pointer */
        xfs_dir2_data_free_t    new;            /* new bestfree entry */
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
@@ -411,7 +427,6 @@ xfs_dir2_data_freeinsert(
               hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
 
-       dfp = xfs_dir3_data_bestfree_p(hdr);
        new.length = dup->length;
        new.offset = cpu_to_be16((char *)dup - (char *)hdr);
 
@@ -444,11 +459,11 @@ xfs_dir2_data_freeinsert(
  */
 STATIC void
 xfs_dir2_data_freeremove(
-       xfs_dir2_data_hdr_t     *hdr,           /* data block header */
-       xfs_dir2_data_free_t    *dfp,           /* bestfree entry pointer */
+       struct xfs_dir2_data_hdr *hdr,          /* data block header */
+       struct xfs_dir2_data_free *bf,          /* bestfree table pointer */
+       struct xfs_dir2_data_free *dfp,         /* bestfree entry pointer */
        int                     *loghead)       /* out: log data header */
 {
-       struct xfs_dir2_data_free *bf;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
@@ -458,7 +473,6 @@ xfs_dir2_data_freeremove(
        /*
         * It's the first entry, slide the next 2 up.
         */
-       bf = xfs_dir3_data_bestfree_p(hdr);
        if (dfp == &bf[0]) {
                bf[0] = bf[1];
                bf[1] = bf[2];
@@ -486,9 +500,9 @@ xfs_dir2_data_freeremove(
  */
 void
 xfs_dir2_data_freescan(
-       xfs_mount_t             *mp,            /* filesystem mount point */
-       xfs_dir2_data_hdr_t     *hdr,           /* data block header */
-       int                     *loghead)       /* out: log data header */
+       struct xfs_inode        *dp,
+       struct xfs_dir2_data_hdr *hdr,
+       int                     *loghead)
 {
        xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* active data entry */
@@ -505,19 +519,19 @@ xfs_dir2_data_freescan(
        /*
         * Start by clearing the table.
         */
-       bf = xfs_dir3_data_bestfree_p(hdr);
+       bf = dp->d_ops->data_bestfree_p(hdr);
        memset(bf, 0, sizeof(*bf) * XFS_DIR2_DATA_FD_COUNT);
        *loghead = 1;
        /*
         * Set up pointers.
         */
-       p = (char *)xfs_dir3_data_entry_p(hdr);
+       p = (char *)dp->d_ops->data_entry_p(hdr);
        if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
            hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC)) {
-               btp = xfs_dir2_block_tail_p(mp, hdr);
+               btp = xfs_dir2_block_tail_p(dp->i_mount, hdr);
                endp = (char *)xfs_dir2_block_leaf_p(btp);
        } else
-               endp = (char *)hdr + mp->m_dirblksize;
+               endp = (char *)hdr + dp->i_mount->m_dirblksize;
        /*
         * Loop over the block's entries.
         */
@@ -529,7 +543,7 @@ xfs_dir2_data_freescan(
                if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
                        ASSERT((char *)dup - (char *)hdr ==
                               be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
-                       xfs_dir2_data_freeinsert(hdr, dup, loghead);
+                       xfs_dir2_data_freeinsert(hdr, bf, dup, loghead);
                        p += be16_to_cpu(dup->length);
                }
                /*
@@ -538,8 +552,8 @@ xfs_dir2_data_freescan(
                else {
                        dep = (xfs_dir2_data_entry_t *)p;
                        ASSERT((char *)dep - (char *)hdr ==
-                              be16_to_cpu(*xfs_dir3_data_entry_tag_p(mp, dep)));
-                       p += xfs_dir3_data_entsize(mp, dep->namelen);
+                              be16_to_cpu(*dp->d_ops->data_entry_tag_p(dep)));
+                       p += dp->d_ops->data_entsize(dep->namelen);
                }
        }
 }
@@ -594,8 +608,8 @@ xfs_dir3_data_init(
        } else
                hdr->magic = cpu_to_be32(XFS_DIR2_DATA_MAGIC);
 
-       bf = xfs_dir3_data_bestfree_p(hdr);
-       bf[0].offset = cpu_to_be16(xfs_dir3_data_entry_offset(hdr));
+       bf = dp->d_ops->data_bestfree_p(hdr);
+       bf[0].offset = cpu_to_be16(dp->d_ops->data_entry_offset);
        for (i = 1; i < XFS_DIR2_DATA_FD_COUNT; i++) {
                bf[i].length = 0;
                bf[i].offset = 0;
@@ -604,17 +618,17 @@ xfs_dir3_data_init(
        /*
         * Set up an unused entry for the block's body.
         */
-       dup = xfs_dir3_data_unused_p(hdr);
+       dup = dp->d_ops->data_unused_p(hdr);
        dup->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG);
 
-       t = mp->m_dirblksize - (uint)xfs_dir3_data_entry_offset(hdr);
+       t = mp->m_dirblksize - (uint)dp->d_ops->data_entry_offset;
        bf[0].length = cpu_to_be16(t);
        dup->length = cpu_to_be16(t);
        *xfs_dir2_data_unused_tag_p(dup) = cpu_to_be16((char *)dup - (char *)hdr);
        /*
         * Log it and return it.
         */
-       xfs_dir2_data_log_header(tp, bp);
+       xfs_dir2_data_log_header(tp, dp, bp);
        xfs_dir2_data_log_unused(tp, bp, dup);
        *bpp = bp;
        return 0;
@@ -626,11 +640,11 @@ xfs_dir3_data_init(
 void
 xfs_dir2_data_log_entry(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp,
        xfs_dir2_data_entry_t   *dep)           /* data entry pointer */
 {
        struct xfs_dir2_data_hdr *hdr = bp->b_addr;
-       struct xfs_mount        *mp = tp->t_mountp;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
@@ -638,7 +652,7 @@ xfs_dir2_data_log_entry(
               hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
 
        xfs_trans_log_buf(tp, bp, (uint)((char *)dep - (char *)hdr),
-               (uint)((char *)(xfs_dir3_data_entry_tag_p(mp, dep) + 1) -
+               (uint)((char *)(dp->d_ops->data_entry_tag_p(dep) + 1) -
                       (char *)hdr - 1));
 }
 
@@ -648,16 +662,19 @@ xfs_dir2_data_log_entry(
 void
 xfs_dir2_data_log_header(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp)
 {
-       xfs_dir2_data_hdr_t     *hdr = bp->b_addr;
+#ifdef DEBUG
+       struct xfs_dir2_data_hdr *hdr = bp->b_addr;
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
+#endif
 
-       xfs_trans_log_buf(tp, bp, 0, xfs_dir3_data_entry_offset(hdr) - 1);
+       xfs_trans_log_buf(tp, bp, 0, dp->d_ops->data_entry_offset - 1);
 }
 
 /*
@@ -698,6 +715,7 @@ xfs_dir2_data_log_unused(
 void
 xfs_dir2_data_make_free(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp,
        xfs_dir2_data_aoff_t    offset,         /* starting byte offset */
        xfs_dir2_data_aoff_t    len,            /* length in bytes */
@@ -735,7 +753,7 @@ xfs_dir2_data_make_free(
         * If this isn't the start of the block, then back up to
         * the previous entry and see if it's free.
         */
-       if (offset > xfs_dir3_data_entry_offset(hdr)) {
+       if (offset > dp->d_ops->data_entry_offset) {
                __be16                  *tagp;  /* tag just before us */
 
                tagp = (__be16 *)((char *)hdr + offset) - 1;
@@ -761,15 +779,15 @@ xfs_dir2_data_make_free(
         * Previous and following entries are both free,
         * merge everything into a single free entry.
         */
-       bf = xfs_dir3_data_bestfree_p(hdr);
+       bf = dp->d_ops->data_bestfree_p(hdr);
        if (prevdup && postdup) {
                xfs_dir2_data_free_t    *dfp2;  /* another bestfree pointer */
 
                /*
                 * See if prevdup and/or postdup are in bestfree table.
                 */
-               dfp = xfs_dir2_data_freefind(hdr, prevdup);
-               dfp2 = xfs_dir2_data_freefind(hdr, postdup);
+               dfp = xfs_dir2_data_freefind(hdr, bf, prevdup);
+               dfp2 = xfs_dir2_data_freefind(hdr, bf, postdup);
                /*
                 * We need a rescan unless there are exactly 2 free entries
                 * namely our two.  Then we know what's happening, otherwise
@@ -797,12 +815,13 @@ xfs_dir2_data_make_free(
                                ASSERT(dfp2 == dfp);
                                dfp2 = &bf[1];
                        }
-                       xfs_dir2_data_freeremove(hdr, dfp2, needlogp);
-                       xfs_dir2_data_freeremove(hdr, dfp, needlogp);
+                       xfs_dir2_data_freeremove(hdr, bf, dfp2, needlogp);
+                       xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
                        /*
                         * Now insert the new entry.
                         */
-                       dfp = xfs_dir2_data_freeinsert(hdr, prevdup, needlogp);
+                       dfp = xfs_dir2_data_freeinsert(hdr, bf, prevdup,
+                                                      needlogp);
                        ASSERT(dfp == &bf[0]);
                        ASSERT(dfp->length == prevdup->length);
                        ASSERT(!dfp[1].length);
@@ -813,7 +832,7 @@ xfs_dir2_data_make_free(
         * The entry before us is free, merge with it.
         */
        else if (prevdup) {
-               dfp = xfs_dir2_data_freefind(hdr, prevdup);
+               dfp = xfs_dir2_data_freefind(hdr, bf, prevdup);
                be16_add_cpu(&prevdup->length, len);
                *xfs_dir2_data_unused_tag_p(prevdup) =
                        cpu_to_be16((char *)prevdup - (char *)hdr);
@@ -824,8 +843,8 @@ xfs_dir2_data_make_free(
                 * the old one and add the new one.
                 */
                if (dfp) {
-                       xfs_dir2_data_freeremove(hdr, dfp, needlogp);
-                       xfs_dir2_data_freeinsert(hdr, prevdup, needlogp);
+                       xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
+                       xfs_dir2_data_freeinsert(hdr, bf, prevdup, needlogp);
                }
                /*
                 * Otherwise we need a scan if the new entry is big enough.
@@ -839,7 +858,7 @@ xfs_dir2_data_make_free(
         * The following entry is free, merge with it.
         */
        else if (postdup) {
-               dfp = xfs_dir2_data_freefind(hdr, postdup);
+               dfp = xfs_dir2_data_freefind(hdr, bf, postdup);
                newdup = (xfs_dir2_data_unused_t *)((char *)hdr + offset);
                newdup->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG);
                newdup->length = cpu_to_be16(len + be16_to_cpu(postdup->length));
@@ -852,8 +871,8 @@ xfs_dir2_data_make_free(
                 * the old one and add the new one.
                 */
                if (dfp) {
-                       xfs_dir2_data_freeremove(hdr, dfp, needlogp);
-                       xfs_dir2_data_freeinsert(hdr, newdup, needlogp);
+                       xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
+                       xfs_dir2_data_freeinsert(hdr, bf, newdup, needlogp);
                }
                /*
                 * Otherwise we need a scan if the new entry is big enough.
@@ -873,7 +892,7 @@ xfs_dir2_data_make_free(
                *xfs_dir2_data_unused_tag_p(newdup) =
                        cpu_to_be16((char *)newdup - (char *)hdr);
                xfs_dir2_data_log_unused(tp, bp, newdup);
-               xfs_dir2_data_freeinsert(hdr, newdup, needlogp);
+               xfs_dir2_data_freeinsert(hdr, bf, newdup, needlogp);
        }
        *needscanp = needscan;
 }
@@ -884,6 +903,7 @@ xfs_dir2_data_make_free(
 void
 xfs_dir2_data_use_free(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp,
        xfs_dir2_data_unused_t  *dup,           /* unused entry */
        xfs_dir2_data_aoff_t    offset,         /* starting offset to use */
@@ -913,9 +933,9 @@ xfs_dir2_data_use_free(
        /*
         * Look up the entry in the bestfree table.
         */
-       dfp = xfs_dir2_data_freefind(hdr, dup);
        oldlen = be16_to_cpu(dup->length);
-       bf = xfs_dir3_data_bestfree_p(hdr);
+       bf = dp->d_ops->data_bestfree_p(hdr);
+       dfp = xfs_dir2_data_freefind(hdr, bf, dup);
        ASSERT(dfp || oldlen <= be16_to_cpu(bf[2].length));
        /*
         * Check for alignment with front and back of the entry.
@@ -932,7 +952,8 @@ xfs_dir2_data_use_free(
                if (dfp) {
                        needscan = (bf[2].offset != 0);
                        if (!needscan)
-                               xfs_dir2_data_freeremove(hdr, dfp, needlogp);
+                               xfs_dir2_data_freeremove(hdr, bf, dfp,
+                                                        needlogp);
                }
        }
        /*
@@ -950,8 +971,9 @@ xfs_dir2_data_use_free(
                 * If it was in the table, remove it and add the new one.
                 */
                if (dfp) {
-                       xfs_dir2_data_freeremove(hdr, dfp, needlogp);
-                       dfp = xfs_dir2_data_freeinsert(hdr, newdup, needlogp);
+                       xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
+                       dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
+                                                      needlogp);
                        ASSERT(dfp != NULL);
                        ASSERT(dfp->length == newdup->length);
                        ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
@@ -977,8 +999,9 @@ xfs_dir2_data_use_free(
                 * If it was in the table, remove it and add the new one.
                 */
                if (dfp) {
-                       xfs_dir2_data_freeremove(hdr, dfp, needlogp);
-                       dfp = xfs_dir2_data_freeinsert(hdr, newdup, needlogp);
+                       xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
+                       dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
+                                                      needlogp);
                        ASSERT(dfp != NULL);
                        ASSERT(dfp->length == newdup->length);
                        ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
@@ -1017,9 +1040,11 @@ xfs_dir2_data_use_free(
                if (dfp) {
                        needscan = (bf[2].length != 0);
                        if (!needscan) {
-                               xfs_dir2_data_freeremove(hdr, dfp, needlogp);
-                               xfs_dir2_data_freeinsert(hdr, newdup, needlogp);
-                               xfs_dir2_data_freeinsert(hdr, newdup2,
+                               xfs_dir2_data_freeremove(hdr, bf, dfp,
+                                                        needlogp);
+                               xfs_dir2_data_freeinsert(hdr, bf, newdup,
+                                                        needlogp);
+                               xfs_dir2_data_freeinsert(hdr, bf, newdup2,
                                                         needlogp);
                        }
                }
index 1021c8356d0836318e300adefc4a35f170d120c3..ae47ec6e16c4031e50ba9ef63884ce216757d41e 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_bmap.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_cksum.h"
 
@@ -52,21 +50,21 @@ static void xfs_dir3_leaf_log_tail(struct xfs_trans *tp, struct xfs_buf *bp);
  * Pop an assert if something is wrong.
  */
 #ifdef DEBUG
-#define        xfs_dir3_leaf_check(mp, bp) \
+#define        xfs_dir3_leaf_check(dp, bp) \
 do { \
-       if (!xfs_dir3_leaf1_check((mp), (bp))) \
+       if (!xfs_dir3_leaf1_check((dp), (bp))) \
                ASSERT(0); \
 } while (0);
 
 STATIC bool
 xfs_dir3_leaf1_check(
-       struct xfs_mount        *mp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp)
 {
        struct xfs_dir2_leaf    *leaf = bp->b_addr;
        struct xfs_dir3_icleaf_hdr leafhdr;
 
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
 
        if (leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) {
                struct xfs_dir3_leaf_hdr *leaf3 = bp->b_addr;
@@ -75,71 +73,16 @@ xfs_dir3_leaf1_check(
        } else if (leafhdr.magic != XFS_DIR2_LEAF1_MAGIC)
                return false;
 
-       return xfs_dir3_leaf_check_int(mp, &leafhdr, leaf);
+       return xfs_dir3_leaf_check_int(dp->i_mount, dp, &leafhdr, leaf);
 }
 #else
-#define        xfs_dir3_leaf_check(mp, bp)
+#define        xfs_dir3_leaf_check(dp, bp)
 #endif
 
-void
-xfs_dir3_leaf_hdr_from_disk(
-       struct xfs_dir3_icleaf_hdr      *to,
-       struct xfs_dir2_leaf            *from)
-{
-       if (from->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC) ||
-           from->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC)) {
-               to->forw = be32_to_cpu(from->hdr.info.forw);
-               to->back = be32_to_cpu(from->hdr.info.back);
-               to->magic = be16_to_cpu(from->hdr.info.magic);
-               to->count = be16_to_cpu(from->hdr.count);
-               to->stale = be16_to_cpu(from->hdr.stale);
-       } else {
-               struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)from;
-
-               to->forw = be32_to_cpu(hdr3->info.hdr.forw);
-               to->back = be32_to_cpu(hdr3->info.hdr.back);
-               to->magic = be16_to_cpu(hdr3->info.hdr.magic);
-               to->count = be16_to_cpu(hdr3->count);
-               to->stale = be16_to_cpu(hdr3->stale);
-       }
-
-       ASSERT(to->magic == XFS_DIR2_LEAF1_MAGIC ||
-              to->magic == XFS_DIR3_LEAF1_MAGIC ||
-              to->magic == XFS_DIR2_LEAFN_MAGIC ||
-              to->magic == XFS_DIR3_LEAFN_MAGIC);
-}
-
-void
-xfs_dir3_leaf_hdr_to_disk(
-       struct xfs_dir2_leaf            *to,
-       struct xfs_dir3_icleaf_hdr      *from)
-{
-       ASSERT(from->magic == XFS_DIR2_LEAF1_MAGIC ||
-              from->magic == XFS_DIR3_LEAF1_MAGIC ||
-              from->magic == XFS_DIR2_LEAFN_MAGIC ||
-              from->magic == XFS_DIR3_LEAFN_MAGIC);
-
-       if (from->magic == XFS_DIR2_LEAF1_MAGIC ||
-           from->magic == XFS_DIR2_LEAFN_MAGIC) {
-               to->hdr.info.forw = cpu_to_be32(from->forw);
-               to->hdr.info.back = cpu_to_be32(from->back);
-               to->hdr.info.magic = cpu_to_be16(from->magic);
-               to->hdr.count = cpu_to_be16(from->count);
-               to->hdr.stale = cpu_to_be16(from->stale);
-       } else {
-               struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)to;
-
-               hdr3->info.hdr.forw = cpu_to_be32(from->forw);
-               hdr3->info.hdr.back = cpu_to_be32(from->back);
-               hdr3->info.hdr.magic = cpu_to_be16(from->magic);
-               hdr3->count = cpu_to_be16(from->count);
-               hdr3->stale = cpu_to_be16(from->stale);
-       }
-}
-
 bool
 xfs_dir3_leaf_check_int(
        struct xfs_mount        *mp,
+       struct xfs_inode        *dp,
        struct xfs_dir3_icleaf_hdr *hdr,
        struct xfs_dir2_leaf    *leaf)
 {
@@ -147,8 +90,21 @@ xfs_dir3_leaf_check_int(
        xfs_dir2_leaf_tail_t    *ltp;
        int                     stale;
        int                     i;
+       const struct xfs_dir_ops *ops;
+       struct xfs_dir3_icleaf_hdr leafhdr;
 
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       /*
+        * we can be passed a null dp here from a verifier, so we need to go the
+        * hard way to get them.
+        */
+       ops = xfs_dir_get_ops(mp, dp);
+
+       if (!hdr) {
+               ops->leaf_hdr_from_disk(&leafhdr, leaf);
+               hdr = &leafhdr;
+       }
+
+       ents = ops->leaf_ents_p(leaf);
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
 
        /*
@@ -156,7 +112,7 @@ xfs_dir3_leaf_check_int(
         * Should factor in the size of the bests table as well.
         * We can deduce a value for that from di_size.
         */
-       if (hdr->count > xfs_dir3_max_leaf_ents(mp, leaf))
+       if (hdr->count > ops->leaf_max_ents(mp))
                return false;
 
        /* Leaves and bests don't overlap in leaf format. */
@@ -192,7 +148,6 @@ xfs_dir3_leaf_verify(
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
        struct xfs_dir2_leaf    *leaf = bp->b_addr;
-       struct xfs_dir3_icleaf_hdr leafhdr;
 
        ASSERT(magic == XFS_DIR2_LEAF1_MAGIC || magic == XFS_DIR2_LEAFN_MAGIC);
 
@@ -214,8 +169,7 @@ xfs_dir3_leaf_verify(
                        return false;
        }
 
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       return xfs_dir3_leaf_check_int(mp, &leafhdr, leaf);
+       return xfs_dir3_leaf_check_int(mp, NULL, NULL, leaf);
 }
 
 static void
@@ -401,7 +355,7 @@ xfs_dir3_leaf_get_buf(
                return error;
 
        xfs_dir3_leaf_init(mp, tp, bp, dp->i_ino, magic);
-       xfs_dir3_leaf_log_header(tp, bp);
+       xfs_dir3_leaf_log_header(tp, dp, bp);
        if (magic == XFS_DIR2_LEAF1_MAGIC)
                xfs_dir3_leaf_log_tail(tp, bp);
        *bpp = bp;
@@ -462,31 +416,31 @@ xfs_dir2_block_to_leaf(
        xfs_dir3_data_check(dp, dbp);
        btp = xfs_dir2_block_tail_p(mp, hdr);
        blp = xfs_dir2_block_leaf_p(btp);
-       bf = xfs_dir3_data_bestfree_p(hdr);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       bf = dp->d_ops->data_bestfree_p(hdr);
+       ents = dp->d_ops->leaf_ents_p(leaf);
 
        /*
         * Set the counts in the leaf header.
         */
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
        leafhdr.count = be32_to_cpu(btp->count);
        leafhdr.stale = be32_to_cpu(btp->stale);
-       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
-       xfs_dir3_leaf_log_header(tp, lbp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, dp, lbp);
 
        /*
         * Could compact these but I think we always do the conversion
         * after squeezing out stale entries.
         */
        memcpy(ents, blp, be32_to_cpu(btp->count) * sizeof(xfs_dir2_leaf_entry_t));
-       xfs_dir3_leaf_log_ents(tp, lbp, 0, leafhdr.count - 1);
+       xfs_dir3_leaf_log_ents(tp, dp, lbp, 0, leafhdr.count - 1);
        needscan = 0;
        needlog = 1;
        /*
         * Make the space formerly occupied by the leaf entries and block
         * tail be free.
         */
-       xfs_dir2_data_make_free(tp, dbp,
+       xfs_dir2_data_make_free(tp, dp, dbp,
                (xfs_dir2_data_aoff_t)((char *)blp - (char *)hdr),
                (xfs_dir2_data_aoff_t)((char *)hdr + mp->m_dirblksize -
                                       (char *)blp),
@@ -502,7 +456,7 @@ xfs_dir2_block_to_leaf(
                hdr->magic = cpu_to_be32(XFS_DIR3_DATA_MAGIC);
 
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        /*
         * Set up leaf tail and bests table.
         */
@@ -514,8 +468,8 @@ xfs_dir2_block_to_leaf(
         * Log the data header and leaf bests table.
         */
        if (needlog)
-               xfs_dir2_data_log_header(tp, dbp);
-       xfs_dir3_leaf_check(mp, lbp);
+               xfs_dir2_data_log_header(tp, dp, dbp);
+       xfs_dir3_leaf_check(dp, lbp);
        xfs_dir3_data_check(dp, dbp);
        xfs_dir3_leaf_log_bests(tp, lbp, 0, 0);
        return 0;
@@ -699,10 +653,10 @@ xfs_dir2_leaf_addname(
        index = xfs_dir2_leaf_search_hash(args, lbp);
        leaf = lbp->b_addr;
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
        bestsp = xfs_dir2_leaf_bests_p(ltp);
-       length = xfs_dir3_data_entsize(mp, args->namelen);
+       length = dp->d_ops->data_entsize(args->namelen);
 
        /*
         * See if there are any entries with the same hash value
@@ -864,7 +818,7 @@ xfs_dir2_leaf_addname(
                else
                        xfs_dir3_leaf_log_bests(tp, lbp, use_block, use_block);
                hdr = dbp->b_addr;
-               bf = xfs_dir3_data_bestfree_p(hdr);
+               bf = dp->d_ops->data_bestfree_p(hdr);
                bestsp[use_block] = bf[0].length;
                grown = 1;
        } else {
@@ -880,7 +834,7 @@ xfs_dir2_leaf_addname(
                        return error;
                }
                hdr = dbp->b_addr;
-               bf = xfs_dir3_data_bestfree_p(hdr);
+               bf = dp->d_ops->data_bestfree_p(hdr);
                grown = 0;
        }
        /*
@@ -893,7 +847,7 @@ xfs_dir2_leaf_addname(
        /*
         * Mark the initial part of our freespace in use for the new entry.
         */
-       xfs_dir2_data_use_free(tp, dbp, dup,
+       xfs_dir2_data_use_free(tp, dp, dbp, dup,
                (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
                &needlog, &needscan);
        /*
@@ -903,20 +857,20 @@ xfs_dir2_leaf_addname(
        dep->inumber = cpu_to_be64(args->inumber);
        dep->namelen = args->namelen;
        memcpy(dep->name, args->name, dep->namelen);
-       xfs_dir3_dirent_put_ftype(mp, dep, args->filetype);
-       tagp = xfs_dir3_data_entry_tag_p(mp, dep);
+       dp->d_ops->data_put_ftype(dep, args->filetype);
+       tagp = dp->d_ops->data_entry_tag_p(dep);
        *tagp = cpu_to_be16((char *)dep - (char *)hdr);
        /*
         * Need to scan fix up the bestfree table.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        /*
         * Need to log the data block's header.
         */
        if (needlog)
-               xfs_dir2_data_log_header(tp, dbp);
-       xfs_dir2_data_log_entry(tp, dbp, dep);
+               xfs_dir2_data_log_header(tp, dp, dbp);
+       xfs_dir2_data_log_entry(tp, dp, dbp, dep);
        /*
         * If the bests table needs to be changed, do it.
         * Log the change unless we've already done that.
@@ -939,10 +893,10 @@ xfs_dir2_leaf_addname(
        /*
         * Log the leaf fields and give up the buffers.
         */
-       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
-       xfs_dir3_leaf_log_header(tp, lbp);
-       xfs_dir3_leaf_log_ents(tp, lbp, lfloglow, lfloghigh);
-       xfs_dir3_leaf_check(mp, lbp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, dp, lbp);
+       xfs_dir3_leaf_log_ents(tp, dp, lbp, lfloglow, lfloghigh);
+       xfs_dir3_leaf_check(dp, lbp);
        xfs_dir3_data_check(dp, dbp);
        return 0;
 }
@@ -962,6 +916,7 @@ xfs_dir3_leaf_compact(
        int             loglow;         /* first leaf entry to log */
        int             to;             /* target leaf index */
        struct xfs_dir2_leaf_entry *ents;
+       struct xfs_inode *dp = args->dp;
 
        leaf = bp->b_addr;
        if (!leafhdr->stale)
@@ -970,7 +925,7 @@ xfs_dir3_leaf_compact(
        /*
         * Compress out the stale entries in place.
         */
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        for (from = to = 0, loglow = -1; from < leafhdr->count; from++) {
                if (ents[from].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
                        continue;
@@ -991,10 +946,10 @@ xfs_dir3_leaf_compact(
        leafhdr->count -= leafhdr->stale;
        leafhdr->stale = 0;
 
-       xfs_dir3_leaf_hdr_to_disk(leaf, leafhdr);
-       xfs_dir3_leaf_log_header(args->trans, bp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, leafhdr);
+       xfs_dir3_leaf_log_header(args->trans, dp, bp);
        if (loglow != -1)
-               xfs_dir3_leaf_log_ents(args->trans, bp, loglow, to - 1);
+               xfs_dir3_leaf_log_ents(args->trans, dp, bp, loglow, to - 1);
 }
 
 /*
@@ -1121,10 +1076,11 @@ xfs_dir3_leaf_log_bests(
  */
 void
 xfs_dir3_leaf_log_ents(
-       xfs_trans_t             *tp,            /* transaction pointer */
-       struct xfs_buf          *bp,            /* leaf buffer */
-       int                     first,          /* first entry to log */
-       int                     last)           /* last entry to log */
+       struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
+       struct xfs_buf          *bp,
+       int                     first,
+       int                     last)
 {
        xfs_dir2_leaf_entry_t   *firstlep;      /* pointer to first entry */
        xfs_dir2_leaf_entry_t   *lastlep;       /* pointer to last entry */
@@ -1136,7 +1092,7 @@ xfs_dir3_leaf_log_ents(
               leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC) ||
               leaf->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC));
 
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        firstlep = &ents[first];
        lastlep = &ents[last];
        xfs_trans_log_buf(tp, bp, (uint)((char *)firstlep - (char *)leaf),
@@ -1149,6 +1105,7 @@ xfs_dir3_leaf_log_ents(
 void
 xfs_dir3_leaf_log_header(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp)
 {
        struct xfs_dir2_leaf    *leaf = bp->b_addr;
@@ -1159,7 +1116,7 @@ xfs_dir3_leaf_log_header(
               leaf->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC));
 
        xfs_trans_log_buf(tp, bp, (uint)((char *)&leaf->hdr - (char *)leaf),
-                         xfs_dir3_leaf_hdr_size(leaf) - 1);
+                         dp->d_ops->leaf_hdr_size - 1);
 }
 
 /*
@@ -1214,9 +1171,9 @@ xfs_dir2_leaf_lookup(
        }
        tp = args->trans;
        dp = args->dp;
-       xfs_dir3_leaf_check(dp->i_mount, lbp);
+       xfs_dir3_leaf_check(dp, lbp);
        leaf = lbp->b_addr;
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        /*
         * Get to the leaf entry and contained data entry address.
         */
@@ -1232,7 +1189,7 @@ xfs_dir2_leaf_lookup(
         * Return the found inode number & CI name if appropriate
         */
        args->inumber = be64_to_cpu(dep->inumber);
-       args->filetype = xfs_dir3_dirent_get_ftype(dp->i_mount, dep);
+       args->filetype = dp->d_ops->data_get_ftype(dep);
        error = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
        xfs_trans_brelse(tp, dbp);
        xfs_trans_brelse(tp, lbp);
@@ -1279,9 +1236,9 @@ xfs_dir2_leaf_lookup_int(
 
        *lbpp = lbp;
        leaf = lbp->b_addr;
-       xfs_dir3_leaf_check(mp, lbp);
-       ents = xfs_dir3_leaf_ents_p(leaf);
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       xfs_dir3_leaf_check(dp, lbp);
+       ents = dp->d_ops->leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
 
        /*
         * Look for the first leaf entry with our hash value.
@@ -1415,9 +1372,9 @@ xfs_dir2_leaf_removename(
        leaf = lbp->b_addr;
        hdr = dbp->b_addr;
        xfs_dir3_data_check(dp, dbp);
-       bf = xfs_dir3_data_bestfree_p(hdr);
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       bf = dp->d_ops->data_bestfree_p(hdr);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        /*
         * Point to the leaf entry, use that to point to the data entry.
         */
@@ -1433,27 +1390,27 @@ xfs_dir2_leaf_removename(
        /*
         * Mark the former data entry unused.
         */
-       xfs_dir2_data_make_free(tp, dbp,
+       xfs_dir2_data_make_free(tp, dp, dbp,
                (xfs_dir2_data_aoff_t)((char *)dep - (char *)hdr),
-               xfs_dir3_data_entsize(mp, dep->namelen), &needlog, &needscan);
+               dp->d_ops->data_entsize(dep->namelen), &needlog, &needscan);
        /*
         * We just mark the leaf entry stale by putting a null in it.
         */
        leafhdr.stale++;
-       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
-       xfs_dir3_leaf_log_header(tp, lbp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, dp, lbp);
 
        lep->address = cpu_to_be32(XFS_DIR2_NULL_DATAPTR);
-       xfs_dir3_leaf_log_ents(tp, lbp, index, index);
+       xfs_dir3_leaf_log_ents(tp, dp, lbp, index, index);
 
        /*
         * Scan the freespace in the data block again if necessary,
         * log the data block header if necessary.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        if (needlog)
-               xfs_dir2_data_log_header(tp, dbp);
+               xfs_dir2_data_log_header(tp, dp, dbp);
        /*
         * If the longest freespace in the data block has changed,
         * put the new value in the bests table and log that.
@@ -1467,7 +1424,7 @@ xfs_dir2_leaf_removename(
         * If the data block is now empty then get rid of the data block.
         */
        if (be16_to_cpu(bf[0].length) ==
-                       mp->m_dirblksize - xfs_dir3_data_entry_offset(hdr)) {
+                       mp->m_dirblksize - dp->d_ops->data_entry_offset) {
                ASSERT(db != mp->m_dirdatablk);
                if ((error = xfs_dir2_shrink_inode(args, db, dbp))) {
                        /*
@@ -1478,7 +1435,7 @@ xfs_dir2_leaf_removename(
                         */
                        if (error == ENOSPC && args->total == 0)
                                error = 0;
-                       xfs_dir3_leaf_check(mp, lbp);
+                       xfs_dir3_leaf_check(dp, lbp);
                        return error;
                }
                dbp = NULL;
@@ -1512,7 +1469,7 @@ xfs_dir2_leaf_removename(
        else if (db != mp->m_dirdatablk)
                dbp = NULL;
 
-       xfs_dir3_leaf_check(mp, lbp);
+       xfs_dir3_leaf_check(dp, lbp);
        /*
         * See if we can convert to block form.
         */
@@ -1547,7 +1504,7 @@ xfs_dir2_leaf_replace(
        }
        dp = args->dp;
        leaf = lbp->b_addr;
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        /*
         * Point to the leaf entry, get data address from it.
         */
@@ -1563,10 +1520,10 @@ xfs_dir2_leaf_replace(
         * Put the new inode number in, log it.
         */
        dep->inumber = cpu_to_be64(args->inumber);
-       xfs_dir3_dirent_put_ftype(dp->i_mount, dep, args->filetype);
+       dp->d_ops->data_put_ftype(dep, args->filetype);
        tp = args->trans;
-       xfs_dir2_data_log_entry(tp, dbp, dep);
-       xfs_dir3_leaf_check(dp->i_mount, lbp);
+       xfs_dir2_data_log_entry(tp, dp, dbp, dep);
+       xfs_dir3_leaf_check(dp, lbp);
        xfs_trans_brelse(tp, lbp);
        return 0;
 }
@@ -1592,8 +1549,8 @@ xfs_dir2_leaf_search_hash(
        struct xfs_dir3_icleaf_hdr leafhdr;
 
        leaf = lbp->b_addr;
-       ents = xfs_dir3_leaf_ents_p(leaf);
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = args->dp->d_ops->leaf_ents_p(leaf);
+       args->dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
 
        /*
         * Note, the table cannot be empty, so we have to go through the loop.
@@ -1661,12 +1618,12 @@ xfs_dir2_leaf_trim_data(
 #ifdef DEBUG
 {
        struct xfs_dir2_data_hdr *hdr = dbp->b_addr;
-       struct xfs_dir2_data_free *bf = xfs_dir3_data_bestfree_p(hdr);
+       struct xfs_dir2_data_free *bf = dp->d_ops->data_bestfree_p(hdr);
 
        ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
               hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC));
        ASSERT(be16_to_cpu(bf[0].length) ==
-              mp->m_dirblksize - xfs_dir3_data_entry_offset(hdr));
+              mp->m_dirblksize - dp->d_ops->data_entry_offset);
        ASSERT(db == be32_to_cpu(ltp->bestcount) - 1);
 }
 #endif
@@ -1782,7 +1739,7 @@ xfs_dir2_node_to_leaf(
                return 0;
        lbp = state->path.blk[0].bp;
        leaf = lbp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
 
        ASSERT(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
               leafhdr.magic == XFS_DIR3_LEAFN_MAGIC);
@@ -1794,7 +1751,7 @@ xfs_dir2_node_to_leaf(
        if (error)
                return error;
        free = fbp->b_addr;
-       xfs_dir3_free_hdr_from_disk(&freehdr, free);
+       dp->d_ops->free_hdr_from_disk(&freehdr, free);
 
        ASSERT(!freehdr.firstdb);
 
@@ -1828,14 +1785,14 @@ xfs_dir2_node_to_leaf(
        /*
         * Set up the leaf bests table.
         */
-       memcpy(xfs_dir2_leaf_bests_p(ltp), xfs_dir3_free_bests_p(mp, free),
+       memcpy(xfs_dir2_leaf_bests_p(ltp), dp->d_ops->free_bests_p(free),
                freehdr.nvalid * sizeof(xfs_dir2_data_off_t));
 
-       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
-       xfs_dir3_leaf_log_header(tp, lbp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, dp, lbp);
        xfs_dir3_leaf_log_bests(tp, lbp, 0, be32_to_cpu(ltp->bestcount) - 1);
        xfs_dir3_leaf_log_tail(tp, lbp);
-       xfs_dir3_leaf_check(mp, lbp);
+       xfs_dir3_leaf_check(dp, lbp);
 
        /*
         * Get rid of the freespace block.
index 4c3dba7ffb7439d250b0af44e4de237a9359a795..56369d4509d5603cff44adeb17902324a547f48b 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_bmap.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_cksum.h"
 
@@ -55,21 +54,21 @@ static int xfs_dir2_node_addname_int(xfs_da_args_t *args,
  * Check internal consistency of a leafn block.
  */
 #ifdef DEBUG
-#define        xfs_dir3_leaf_check(mp, bp) \
+#define        xfs_dir3_leaf_check(dp, bp) \
 do { \
-       if (!xfs_dir3_leafn_check((mp), (bp))) \
+       if (!xfs_dir3_leafn_check((dp), (bp))) \
                ASSERT(0); \
 } while (0);
 
 static bool
 xfs_dir3_leafn_check(
-       struct xfs_mount        *mp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp)
 {
        struct xfs_dir2_leaf    *leaf = bp->b_addr;
        struct xfs_dir3_icleaf_hdr leafhdr;
 
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
 
        if (leafhdr.magic == XFS_DIR3_LEAFN_MAGIC) {
                struct xfs_dir3_leaf_hdr *leaf3 = bp->b_addr;
@@ -78,10 +77,10 @@ xfs_dir3_leafn_check(
        } else if (leafhdr.magic != XFS_DIR2_LEAFN_MAGIC)
                return false;
 
-       return xfs_dir3_leaf_check_int(mp, &leafhdr, leaf);
+       return xfs_dir3_leaf_check_int(dp->i_mount, dp, &leafhdr, leaf);
 }
 #else
-#define        xfs_dir3_leaf_check(mp, bp)
+#define        xfs_dir3_leaf_check(dp, bp)
 #endif
 
 static bool
@@ -193,53 +192,6 @@ xfs_dir2_free_try_read(
        return __xfs_dir3_free_read(tp, dp, fbno, -2, bpp);
 }
 
-
-void
-xfs_dir3_free_hdr_from_disk(
-       struct xfs_dir3_icfree_hdr      *to,
-       struct xfs_dir2_free            *from)
-{
-       if (from->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC)) {
-               to->magic = be32_to_cpu(from->hdr.magic);
-               to->firstdb = be32_to_cpu(from->hdr.firstdb);
-               to->nvalid = be32_to_cpu(from->hdr.nvalid);
-               to->nused = be32_to_cpu(from->hdr.nused);
-       } else {
-               struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)from;
-
-               to->magic = be32_to_cpu(hdr3->hdr.magic);
-               to->firstdb = be32_to_cpu(hdr3->firstdb);
-               to->nvalid = be32_to_cpu(hdr3->nvalid);
-               to->nused = be32_to_cpu(hdr3->nused);
-       }
-
-       ASSERT(to->magic == XFS_DIR2_FREE_MAGIC ||
-              to->magic == XFS_DIR3_FREE_MAGIC);
-}
-
-static void
-xfs_dir3_free_hdr_to_disk(
-       struct xfs_dir2_free            *to,
-       struct xfs_dir3_icfree_hdr      *from)
-{
-       ASSERT(from->magic == XFS_DIR2_FREE_MAGIC ||
-              from->magic == XFS_DIR3_FREE_MAGIC);
-
-       if (from->magic == XFS_DIR2_FREE_MAGIC) {
-               to->hdr.magic = cpu_to_be32(from->magic);
-               to->hdr.firstdb = cpu_to_be32(from->firstdb);
-               to->hdr.nvalid = cpu_to_be32(from->nvalid);
-               to->hdr.nused = cpu_to_be32(from->nused);
-       } else {
-               struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)to;
-
-               hdr3->hdr.magic = cpu_to_be32(from->magic);
-               hdr3->firstdb = cpu_to_be32(from->firstdb);
-               hdr3->nvalid = cpu_to_be32(from->nvalid);
-               hdr3->nused = cpu_to_be32(from->nused);
-       }
-}
-
 static int
 xfs_dir3_free_get_buf(
        struct xfs_trans        *tp,
@@ -277,7 +229,7 @@ xfs_dir3_free_get_buf(
                uuid_copy(&hdr3->hdr.uuid, &mp->m_sb.sb_uuid);
        } else
                hdr.magic = XFS_DIR2_FREE_MAGIC;
-       xfs_dir3_free_hdr_to_disk(bp->b_addr, &hdr);
+       dp->d_ops->free_hdr_to_disk(bp->b_addr, &hdr);
        *bpp = bp;
        return 0;
 }
@@ -288,6 +240,7 @@ xfs_dir3_free_get_buf(
 STATIC void
 xfs_dir2_free_log_bests(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp,
        int                     first,          /* first entry to log */
        int                     last)           /* last entry to log */
@@ -296,7 +249,7 @@ xfs_dir2_free_log_bests(
        __be16                  *bests;
 
        free = bp->b_addr;
-       bests = xfs_dir3_free_bests_p(tp->t_mountp, free);
+       bests = dp->d_ops->free_bests_p(free);
        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC) ||
               free->hdr.magic == cpu_to_be32(XFS_DIR3_FREE_MAGIC));
        xfs_trans_log_buf(tp, bp,
@@ -311,6 +264,7 @@ xfs_dir2_free_log_bests(
 static void
 xfs_dir2_free_log_header(
        struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
        struct xfs_buf          *bp)
 {
 #ifdef DEBUG
@@ -320,7 +274,7 @@ xfs_dir2_free_log_header(
        ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC) ||
               free->hdr.magic == cpu_to_be32(XFS_DIR3_FREE_MAGIC));
 #endif
-       xfs_trans_log_buf(tp, bp, 0, xfs_dir3_free_hdr_size(tp->t_mountp) - 1);
+       xfs_trans_log_buf(tp, bp, 0, dp->d_ops->free_hdr_size - 1);
 }
 
 /*
@@ -369,7 +323,7 @@ xfs_dir2_leaf_to_node(
                return error;
 
        free = fbp->b_addr;
-       xfs_dir3_free_hdr_from_disk(&freehdr, free);
+       dp->d_ops->free_hdr_from_disk(&freehdr, free);
        leaf = lbp->b_addr;
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
        ASSERT(be32_to_cpu(ltp->bestcount) <=
@@ -380,7 +334,7 @@ xfs_dir2_leaf_to_node(
         * Count active entries.
         */
        from = xfs_dir2_leaf_bests_p(ltp);
-       to = xfs_dir3_free_bests_p(mp, free);
+       to = dp->d_ops->free_bests_p(free);
        for (i = n = 0; i < be32_to_cpu(ltp->bestcount); i++, from++, to++) {
                if ((off = be16_to_cpu(*from)) != NULLDATAOFF)
                        n++;
@@ -393,9 +347,9 @@ xfs_dir2_leaf_to_node(
        freehdr.nused = n;
        freehdr.nvalid = be32_to_cpu(ltp->bestcount);
 
-       xfs_dir3_free_hdr_to_disk(fbp->b_addr, &freehdr);
-       xfs_dir2_free_log_bests(tp, fbp, 0, freehdr.nvalid - 1);
-       xfs_dir2_free_log_header(tp, fbp);
+       dp->d_ops->free_hdr_to_disk(fbp->b_addr, &freehdr);
+       xfs_dir2_free_log_bests(tp, dp, fbp, 0, freehdr.nvalid - 1);
+       xfs_dir2_free_log_header(tp, dp, fbp);
 
        /*
         * Converting the leaf to a leafnode is just a matter of changing the
@@ -409,8 +363,8 @@ xfs_dir2_leaf_to_node(
                leaf->hdr.info.magic = cpu_to_be16(XFS_DIR3_LEAFN_MAGIC);
        lbp->b_ops = &xfs_dir3_leafn_buf_ops;
        xfs_trans_buf_set_type(tp, lbp, XFS_BLFT_DIR_LEAFN_BUF);
-       xfs_dir3_leaf_log_header(tp, lbp);
-       xfs_dir3_leaf_check(mp, lbp);
+       xfs_dir3_leaf_log_header(tp, dp, lbp);
+       xfs_dir3_leaf_check(dp, lbp);
        return 0;
 }
 
@@ -443,8 +397,8 @@ xfs_dir2_leafn_add(
        mp = dp->i_mount;
        tp = args->trans;
        leaf = bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
 
        /*
         * Quick check just to make sure we are not going to index
@@ -460,7 +414,7 @@ xfs_dir2_leafn_add(
         * a compact.
         */
 
-       if (leafhdr.count == xfs_dir3_max_leaf_ents(mp, leaf)) {
+       if (leafhdr.count == dp->d_ops->leaf_max_ents(mp)) {
                if (!leafhdr.stale)
                        return XFS_ERROR(ENOSPC);
                compact = leafhdr.stale > 1;
@@ -498,30 +452,30 @@ xfs_dir2_leafn_add(
        lep->address = cpu_to_be32(xfs_dir2_db_off_to_dataptr(mp,
                                args->blkno, args->index));
 
-       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
-       xfs_dir3_leaf_log_header(tp, bp);
-       xfs_dir3_leaf_log_ents(tp, bp, lfloglow, lfloghigh);
-       xfs_dir3_leaf_check(mp, bp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, dp, bp);
+       xfs_dir3_leaf_log_ents(tp, dp, bp, lfloglow, lfloghigh);
+       xfs_dir3_leaf_check(dp, bp);
        return 0;
 }
 
 #ifdef DEBUG
 static void
 xfs_dir2_free_hdr_check(
-       struct xfs_mount *mp,
+       struct xfs_inode *dp,
        struct xfs_buf  *bp,
        xfs_dir2_db_t   db)
 {
        struct xfs_dir3_icfree_hdr hdr;
 
-       xfs_dir3_free_hdr_from_disk(&hdr, bp->b_addr);
+       dp->d_ops->free_hdr_from_disk(&hdr, bp->b_addr);
 
-       ASSERT((hdr.firstdb % xfs_dir3_free_max_bests(mp)) == 0);
+       ASSERT((hdr.firstdb % dp->d_ops->free_max_bests(dp->i_mount)) == 0);
        ASSERT(hdr.firstdb <= db);
        ASSERT(db < hdr.firstdb + hdr.nvalid);
 }
 #else
-#define xfs_dir2_free_hdr_check(mp, dp, db)
+#define xfs_dir2_free_hdr_check(dp, bp, db)
 #endif /* DEBUG */
 
 /*
@@ -530,6 +484,7 @@ xfs_dir2_free_hdr_check(
  */
 xfs_dahash_t                                   /* hash value */
 xfs_dir2_leafn_lasthash(
+       struct xfs_inode *dp,
        struct xfs_buf  *bp,                    /* leaf buffer */
        int             *count)                 /* count of entries in leaf */
 {
@@ -537,7 +492,7 @@ xfs_dir2_leafn_lasthash(
        struct xfs_dir2_leaf_entry *ents;
        struct xfs_dir3_icleaf_hdr leafhdr;
 
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
 
        ASSERT(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
               leafhdr.magic == XFS_DIR3_LEAFN_MAGIC);
@@ -547,7 +502,7 @@ xfs_dir2_leafn_lasthash(
        if (!leafhdr.count)
                return 0;
 
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
        return be32_to_cpu(ents[leafhdr.count - 1].hashval);
 }
 
@@ -584,10 +539,10 @@ xfs_dir2_leafn_lookup_for_addname(
        tp = args->trans;
        mp = dp->i_mount;
        leaf = bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
 
-       xfs_dir3_leaf_check(mp, bp);
+       xfs_dir3_leaf_check(dp, bp);
        ASSERT(leafhdr.count > 0);
 
        /*
@@ -605,7 +560,7 @@ xfs_dir2_leafn_lookup_for_addname(
                ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC) ||
                       free->hdr.magic == cpu_to_be32(XFS_DIR3_FREE_MAGIC));
        }
-       length = xfs_dir3_data_entsize(mp, args->namelen);
+       length = dp->d_ops->data_entsize(args->namelen);
        /*
         * Loop over leaf entries with the right hash value.
         */
@@ -637,7 +592,7 @@ xfs_dir2_leafn_lookup_for_addname(
                         * Convert the data block to the free block
                         * holding its freespace information.
                         */
-                       newfdb = xfs_dir2_db_to_fdb(mp, newdb);
+                       newfdb = dp->d_ops->db_to_fdb(mp, newdb);
                        /*
                         * If it's not the one we have in hand, read it in.
                         */
@@ -655,16 +610,16 @@ xfs_dir2_leafn_lookup_for_addname(
                                        return error;
                                free = curbp->b_addr;
 
-                               xfs_dir2_free_hdr_check(mp, curbp, curdb);
+                               xfs_dir2_free_hdr_check(dp, curbp, curdb);
                        }
                        /*
                         * Get the index for our entry.
                         */
-                       fi = xfs_dir2_db_to_fdindex(mp, curdb);
+                       fi = dp->d_ops->db_to_fdindex(mp, curdb);
                        /*
                         * If it has room, return it.
                         */
-                       bests = xfs_dir3_free_bests_p(mp, free);
+                       bests = dp->d_ops->free_bests_p(free);
                        if (unlikely(bests[fi] == cpu_to_be16(NULLDATAOFF))) {
                                XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
                                                        XFS_ERRLEVEL_LOW, mp);
@@ -734,10 +689,10 @@ xfs_dir2_leafn_lookup_for_entry(
        tp = args->trans;
        mp = dp->i_mount;
        leaf = bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
 
-       xfs_dir3_leaf_check(mp, bp);
+       xfs_dir3_leaf_check(dp, bp);
        ASSERT(leafhdr.count > 0);
 
        /*
@@ -816,7 +771,7 @@ xfs_dir2_leafn_lookup_for_entry(
                                xfs_trans_brelse(tp, state->extrablk.bp);
                        args->cmpresult = cmp;
                        args->inumber = be64_to_cpu(dep->inumber);
-                       args->filetype = xfs_dir3_dirent_get_ftype(mp, dep);
+                       args->filetype = dp->d_ops->data_get_ftype(dep);
                        *indexp = index;
                        state->extravalid = 1;
                        state->extrablk.bp = curbp;
@@ -907,7 +862,7 @@ xfs_dir3_leafn_moveents(
        if (start_d < dhdr->count) {
                memmove(&dents[start_d + count], &dents[start_d],
                        (dhdr->count - start_d) * sizeof(xfs_dir2_leaf_entry_t));
-               xfs_dir3_leaf_log_ents(tp, bp_d, start_d + count,
+               xfs_dir3_leaf_log_ents(tp, args->dp, bp_d, start_d + count,
                                       count + dhdr->count - 1);
        }
        /*
@@ -929,7 +884,8 @@ xfs_dir3_leafn_moveents(
         */
        memcpy(&dents[start_d], &sents[start_s],
                count * sizeof(xfs_dir2_leaf_entry_t));
-       xfs_dir3_leaf_log_ents(tp, bp_d, start_d, start_d + count - 1);
+       xfs_dir3_leaf_log_ents(tp, args->dp, bp_d,
+                              start_d, start_d + count - 1);
 
        /*
         * If there are source entries after the ones we copied,
@@ -938,7 +894,8 @@ xfs_dir3_leafn_moveents(
        if (start_s + count < shdr->count) {
                memmove(&sents[start_s], &sents[start_s + count],
                        count * sizeof(xfs_dir2_leaf_entry_t));
-               xfs_dir3_leaf_log_ents(tp, bp_s, start_s, start_s + count - 1);
+               xfs_dir3_leaf_log_ents(tp, args->dp, bp_s,
+                                      start_s, start_s + count - 1);
        }
 
        /*
@@ -956,6 +913,7 @@ xfs_dir3_leafn_moveents(
  */
 int                                            /* sort order */
 xfs_dir2_leafn_order(
+       struct xfs_inode        *dp,
        struct xfs_buf          *leaf1_bp,              /* leaf1 buffer */
        struct xfs_buf          *leaf2_bp)              /* leaf2 buffer */
 {
@@ -966,10 +924,10 @@ xfs_dir2_leafn_order(
        struct xfs_dir3_icleaf_hdr hdr1;
        struct xfs_dir3_icleaf_hdr hdr2;
 
-       xfs_dir3_leaf_hdr_from_disk(&hdr1, leaf1);
-       xfs_dir3_leaf_hdr_from_disk(&hdr2, leaf2);
-       ents1 = xfs_dir3_leaf_ents_p(leaf1);
-       ents2 = xfs_dir3_leaf_ents_p(leaf2);
+       dp->d_ops->leaf_hdr_from_disk(&hdr1, leaf1);
+       dp->d_ops->leaf_hdr_from_disk(&hdr2, leaf2);
+       ents1 = dp->d_ops->leaf_ents_p(leaf1);
+       ents2 = dp->d_ops->leaf_ents_p(leaf2);
 
        if (hdr1.count > 0 && hdr2.count > 0 &&
            (be32_to_cpu(ents2[0].hashval) < be32_to_cpu(ents1[0].hashval) ||
@@ -1007,12 +965,13 @@ xfs_dir2_leafn_rebalance(
        struct xfs_dir2_leaf_entry *ents2;
        struct xfs_dir3_icleaf_hdr hdr1;
        struct xfs_dir3_icleaf_hdr hdr2;
+       struct xfs_inode        *dp = state->args->dp;
 
        args = state->args;
        /*
         * If the block order is wrong, swap the arguments.
         */
-       if ((swap = xfs_dir2_leafn_order(blk1->bp, blk2->bp))) {
+       if ((swap = xfs_dir2_leafn_order(dp, blk1->bp, blk2->bp))) {
                xfs_da_state_blk_t      *tmp;   /* temp for block swap */
 
                tmp = blk1;
@@ -1021,10 +980,10 @@ xfs_dir2_leafn_rebalance(
        }
        leaf1 = blk1->bp->b_addr;
        leaf2 = blk2->bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&hdr1, leaf1);
-       xfs_dir3_leaf_hdr_from_disk(&hdr2, leaf2);
-       ents1 = xfs_dir3_leaf_ents_p(leaf1);
-       ents2 = xfs_dir3_leaf_ents_p(leaf2);
+       dp->d_ops->leaf_hdr_from_disk(&hdr1, leaf1);
+       dp->d_ops->leaf_hdr_from_disk(&hdr2, leaf2);
+       ents1 = dp->d_ops->leaf_ents_p(leaf1);
+       ents2 = dp->d_ops->leaf_ents_p(leaf2);
 
        oldsum = hdr1.count + hdr2.count;
 #if defined(DEBUG) || defined(XFS_WARN)
@@ -1070,13 +1029,13 @@ xfs_dir2_leafn_rebalance(
        ASSERT(hdr1.stale + hdr2.stale == oldstale);
 
        /* log the changes made when moving the entries */
-       xfs_dir3_leaf_hdr_to_disk(leaf1, &hdr1);
-       xfs_dir3_leaf_hdr_to_disk(leaf2, &hdr2);
-       xfs_dir3_leaf_log_header(args->trans, blk1->bp);
-       xfs_dir3_leaf_log_header(args->trans, blk2->bp);
+       dp->d_ops->leaf_hdr_to_disk(leaf1, &hdr1);
+       dp->d_ops->leaf_hdr_to_disk(leaf2, &hdr2);
+       xfs_dir3_leaf_log_header(args->trans, dp, blk1->bp);
+       xfs_dir3_leaf_log_header(args->trans, dp, blk2->bp);
 
-       xfs_dir3_leaf_check(args->dp->i_mount, blk1->bp);
-       xfs_dir3_leaf_check(args->dp->i_mount, blk2->bp);
+       xfs_dir3_leaf_check(dp, blk1->bp);
+       xfs_dir3_leaf_check(dp, blk2->bp);
 
        /*
         * Mark whether we're inserting into the old or new leaf.
@@ -1097,11 +1056,11 @@ xfs_dir2_leafn_rebalance(
         * Finally sanity check just to make sure we are not returning a
         * negative index
         */
-       if(blk2->index < 0) {
+       if (blk2->index < 0) {
                state->inleaf = 1;
                blk2->index = 0;
-               xfs_alert(args->dp->i_mount,
-       "%s: picked the wrong leaf? reverting original leaf: blk1->index %d\n",
+               xfs_alert(dp->i_mount,
+       "%s: picked the wrong leaf? reverting original leaf: blk1->index %d",
                        __func__, blk1->index);
        }
 }
@@ -1120,17 +1079,17 @@ xfs_dir3_data_block_free(
        int                     logfree = 0;
        __be16                  *bests;
        struct xfs_dir3_icfree_hdr freehdr;
+       struct xfs_inode        *dp = args->dp;
 
-       xfs_dir3_free_hdr_from_disk(&freehdr, free);
-
-       bests = xfs_dir3_free_bests_p(tp->t_mountp, free);
+       dp->d_ops->free_hdr_from_disk(&freehdr, free);
+       bests = dp->d_ops->free_bests_p(free);
        if (hdr) {
                /*
                 * Data block is not empty, just set the free entry to the new
                 * value.
                 */
                bests[findex] = cpu_to_be16(longest);
-               xfs_dir2_free_log_bests(tp, fbp, findex, findex);
+               xfs_dir2_free_log_bests(tp, dp, fbp, findex, findex);
                return 0;
        }
 
@@ -1157,8 +1116,8 @@ xfs_dir3_data_block_free(
                logfree = 1;
        }
 
-       xfs_dir3_free_hdr_to_disk(free, &freehdr);
-       xfs_dir2_free_log_header(tp, fbp);
+       dp->d_ops->free_hdr_to_disk(free, &freehdr);
+       xfs_dir2_free_log_header(tp, dp, fbp);
 
        /*
         * If there are no useful entries left in the block, get rid of the
@@ -1182,7 +1141,7 @@ xfs_dir3_data_block_free(
 
        /* Log the free entry that changed, unless we got rid of it.  */
        if (logfree)
-               xfs_dir2_free_log_bests(tp, fbp, findex, findex);
+               xfs_dir2_free_log_bests(tp, dp, fbp, findex, findex);
        return 0;
 }
 
@@ -1222,8 +1181,8 @@ xfs_dir2_leafn_remove(
        tp = args->trans;
        mp = dp->i_mount;
        leaf = bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
 
        /*
         * Point to the entry we're removing.
@@ -1243,11 +1202,11 @@ xfs_dir2_leafn_remove(
         * Log the leaf block changes.
         */
        leafhdr.stale++;
-       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
-       xfs_dir3_leaf_log_header(tp, bp);
+       dp->d_ops->leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, dp, bp);
 
        lep->address = cpu_to_be32(XFS_DIR2_NULL_DATAPTR);
-       xfs_dir3_leaf_log_ents(tp, bp, index, index);
+       xfs_dir3_leaf_log_ents(tp, dp, bp, index, index);
 
        /*
         * Make the data entry free.  Keep track of the longest freespace
@@ -1256,19 +1215,19 @@ xfs_dir2_leafn_remove(
        dbp = dblk->bp;
        hdr = dbp->b_addr;
        dep = (xfs_dir2_data_entry_t *)((char *)hdr + off);
-       bf = xfs_dir3_data_bestfree_p(hdr);
+       bf = dp->d_ops->data_bestfree_p(hdr);
        longest = be16_to_cpu(bf[0].length);
        needlog = needscan = 0;
-       xfs_dir2_data_make_free(tp, dbp, off,
-               xfs_dir3_data_entsize(mp, dep->namelen), &needlog, &needscan);
+       xfs_dir2_data_make_free(tp, dp, dbp, off,
+               dp->d_ops->data_entsize(dep->namelen), &needlog, &needscan);
        /*
         * Rescan the data block freespaces for bestfree.
         * Log the data block header if needed.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        if (needlog)
-               xfs_dir2_data_log_header(tp, dbp);
+               xfs_dir2_data_log_header(tp, dp, dbp);
        xfs_dir3_data_check(dp, dbp);
        /*
         * If the longest data block freespace changes, need to update
@@ -1285,7 +1244,7 @@ xfs_dir2_leafn_remove(
                 * Convert the data block number to a free block,
                 * read in the free block.
                 */
-               fdb = xfs_dir2_db_to_fdb(mp, db);
+               fdb = dp->d_ops->db_to_fdb(mp, db);
                error = xfs_dir2_free_read(tp, dp, xfs_dir2_db_to_da(mp, fdb),
                                           &fbp);
                if (error)
@@ -1294,22 +1253,22 @@ xfs_dir2_leafn_remove(
 #ifdef DEBUG
        {
                struct xfs_dir3_icfree_hdr freehdr;
-               xfs_dir3_free_hdr_from_disk(&freehdr, free);
-               ASSERT(freehdr.firstdb == xfs_dir3_free_max_bests(mp) *
+               dp->d_ops->free_hdr_from_disk(&freehdr, free);
+               ASSERT(freehdr.firstdb == dp->d_ops->free_max_bests(mp) *
                                          (fdb - XFS_DIR2_FREE_FIRSTDB(mp)));
        }
 #endif
                /*
                 * Calculate which entry we need to fix.
                 */
-               findex = xfs_dir2_db_to_fdindex(mp, db);
+               findex = dp->d_ops->db_to_fdindex(mp, db);
                longest = be16_to_cpu(bf[0].length);
                /*
                 * If the data block is now empty we can get rid of it
                 * (usually).
                 */
                if (longest == mp->m_dirblksize -
-                              xfs_dir3_data_entry_offset(hdr)) {
+                              dp->d_ops->data_entry_offset) {
                        /*
                         * Try to punch out the data block.
                         */
@@ -1336,12 +1295,12 @@ xfs_dir2_leafn_remove(
                        return error;
        }
 
-       xfs_dir3_leaf_check(mp, bp);
+       xfs_dir3_leaf_check(dp, bp);
        /*
         * Return indication of whether this leaf block is empty enough
         * to justify trying to join it with a neighbor.
         */
-       *rval = (xfs_dir3_leaf_hdr_size(leaf) +
+       *rval = (dp->d_ops->leaf_hdr_size +
                 (uint)sizeof(ents[0]) * (leafhdr.count - leafhdr.stale)) <
                mp->m_dir_magicpct;
        return 0;
@@ -1360,13 +1319,14 @@ xfs_dir2_leafn_split(
        xfs_dablk_t             blkno;          /* new leaf block number */
        int                     error;          /* error return value */
        xfs_mount_t             *mp;            /* filesystem mount point */
+       struct xfs_inode        *dp;
 
        /*
         * Allocate space for a new leaf node.
         */
        args = state->args;
-       mp = args->dp->i_mount;
-       ASSERT(args != NULL);
+       dp = args->dp;
+       mp = dp->i_mount;
        ASSERT(oldblk->magic == XFS_DIR2_LEAFN_MAGIC);
        error = xfs_da_grow_inode(args, &blkno);
        if (error) {
@@ -1401,10 +1361,10 @@ xfs_dir2_leafn_split(
        /*
         * Update last hashval in each block since we added the name.
         */
-       oldblk->hashval = xfs_dir2_leafn_lasthash(oldblk->bp, NULL);
-       newblk->hashval = xfs_dir2_leafn_lasthash(newblk->bp, NULL);
-       xfs_dir3_leaf_check(mp, oldblk->bp);
-       xfs_dir3_leaf_check(mp, newblk->bp);
+       oldblk->hashval = xfs_dir2_leafn_lasthash(dp, oldblk->bp, NULL);
+       newblk->hashval = xfs_dir2_leafn_lasthash(dp, newblk->bp, NULL);
+       xfs_dir3_leaf_check(dp, oldblk->bp);
+       xfs_dir3_leaf_check(dp, newblk->bp);
        return error;
 }
 
@@ -1434,6 +1394,7 @@ xfs_dir2_leafn_toosmall(
        int                     rval;           /* result from path_shift */
        struct xfs_dir3_icleaf_hdr leafhdr;
        struct xfs_dir2_leaf_entry *ents;
+       struct xfs_inode        *dp = state->args->dp;
 
        /*
         * Check for the degenerate case of the block being over 50% full.
@@ -1442,12 +1403,12 @@ xfs_dir2_leafn_toosmall(
         */
        blk = &state->path.blk[state->path.active - 1];
        leaf = blk->bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
-       xfs_dir3_leaf_check(state->args->dp->i_mount, blk->bp);
+       dp->d_ops->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = dp->d_ops->leaf_ents_p(leaf);
+       xfs_dir3_leaf_check(dp, blk->bp);
 
        count = leafhdr.count - leafhdr.stale;
-       bytes = xfs_dir3_leaf_hdr_size(leaf) + count * sizeof(ents[0]);
+       bytes = dp->d_ops->leaf_hdr_size + count * sizeof(ents[0]);
        if (bytes > (state->blocksize >> 1)) {
                /*
                 * Blk over 50%, don't try to join.
@@ -1492,7 +1453,7 @@ xfs_dir2_leafn_toosmall(
                /*
                 * Read the sibling leaf block.
                 */
-               error = xfs_dir3_leafn_read(state->args->trans, state->args->dp,
+               error = xfs_dir3_leafn_read(state->args->trans, dp,
                                            blkno, -1, &bp);
                if (error)
                        return error;
@@ -1504,8 +1465,8 @@ xfs_dir2_leafn_toosmall(
                bytes = state->blocksize - (state->blocksize >> 2);
 
                leaf = bp->b_addr;
-               xfs_dir3_leaf_hdr_from_disk(&hdr2, leaf);
-               ents = xfs_dir3_leaf_ents_p(leaf);
+               dp->d_ops->leaf_hdr_from_disk(&hdr2, leaf);
+               ents = dp->d_ops->leaf_ents_p(leaf);
                count += hdr2.count - hdr2.stale;
                bytes -= count * sizeof(ents[0]);
 
@@ -1559,6 +1520,7 @@ xfs_dir2_leafn_unbalance(
        struct xfs_dir3_icleaf_hdr drophdr;
        struct xfs_dir2_leaf_entry *sents;
        struct xfs_dir2_leaf_entry *dents;
+       struct xfs_inode        *dp = state->args->dp;
 
        args = state->args;
        ASSERT(drop_blk->magic == XFS_DIR2_LEAFN_MAGIC);
@@ -1566,10 +1528,10 @@ xfs_dir2_leafn_unbalance(
        drop_leaf = drop_blk->bp->b_addr;
        save_leaf = save_blk->bp->b_addr;
 
-       xfs_dir3_leaf_hdr_from_disk(&savehdr, save_leaf);
-       xfs_dir3_leaf_hdr_from_disk(&drophdr, drop_leaf);
-       sents = xfs_dir3_leaf_ents_p(save_leaf);
-       dents = xfs_dir3_leaf_ents_p(drop_leaf);
+       dp->d_ops->leaf_hdr_from_disk(&savehdr, save_leaf);
+       dp->d_ops->leaf_hdr_from_disk(&drophdr, drop_leaf);
+       sents = dp->d_ops->leaf_ents_p(save_leaf);
+       dents = dp->d_ops->leaf_ents_p(drop_leaf);
 
        /*
         * If there are any stale leaf entries, take this opportunity
@@ -1584,7 +1546,7 @@ xfs_dir2_leafn_unbalance(
         * Move the entries from drop to the appropriate end of save.
         */
        drop_blk->hashval = be32_to_cpu(dents[drophdr.count - 1].hashval);
-       if (xfs_dir2_leafn_order(save_blk->bp, drop_blk->bp))
+       if (xfs_dir2_leafn_order(dp, save_blk->bp, drop_blk->bp))
                xfs_dir3_leafn_moveents(args, drop_blk->bp, &drophdr, dents, 0,
                                        save_blk->bp, &savehdr, sents, 0,
                                        drophdr.count);
@@ -1595,13 +1557,13 @@ xfs_dir2_leafn_unbalance(
        save_blk->hashval = be32_to_cpu(sents[savehdr.count - 1].hashval);
 
        /* log the changes made when moving the entries */
-       xfs_dir3_leaf_hdr_to_disk(save_leaf, &savehdr);
-       xfs_dir3_leaf_hdr_to_disk(drop_leaf, &drophdr);
-       xfs_dir3_leaf_log_header(args->trans, save_blk->bp);
-       xfs_dir3_leaf_log_header(args->trans, drop_blk->bp);
+       dp->d_ops->leaf_hdr_to_disk(save_leaf, &savehdr);
+       dp->d_ops->leaf_hdr_to_disk(drop_leaf, &drophdr);
+       xfs_dir3_leaf_log_header(args->trans, dp, save_blk->bp);
+       xfs_dir3_leaf_log_header(args->trans, dp, drop_blk->bp);
 
-       xfs_dir3_leaf_check(args->dp->i_mount, save_blk->bp);
-       xfs_dir3_leaf_check(args->dp->i_mount, drop_blk->bp);
+       xfs_dir3_leaf_check(dp, save_blk->bp);
+       xfs_dir3_leaf_check(dp, drop_blk->bp);
 }
 
 /*
@@ -1712,7 +1674,7 @@ xfs_dir2_node_addname_int(
        dp = args->dp;
        mp = dp->i_mount;
        tp = args->trans;
-       length = xfs_dir3_data_entsize(mp, args->namelen);
+       length = dp->d_ops->data_entsize(args->namelen);
        /*
         * If we came in with a freespace block that means that lookup
         * found an entry with our hash value.  This is the freespace
@@ -1726,8 +1688,8 @@ xfs_dir2_node_addname_int(
                ifbno = fblk->blkno;
                free = fbp->b_addr;
                findex = fblk->index;
-               bests = xfs_dir3_free_bests_p(mp, free);
-               xfs_dir3_free_hdr_from_disk(&freehdr, free);
+               bests = dp->d_ops->free_bests_p(free);
+               dp->d_ops->free_hdr_from_disk(&freehdr, free);
 
                /*
                 * This means the free entry showed that the data block had
@@ -1819,8 +1781,8 @@ xfs_dir2_node_addname_int(
                 * and the freehdr are actually initialised if they are placed
                 * there, so we have to do it here to avoid warnings. Blech.
                 */
-               bests = xfs_dir3_free_bests_p(mp, free);
-               xfs_dir3_free_hdr_from_disk(&freehdr, free);
+               bests = dp->d_ops->free_bests_p(free);
+               dp->d_ops->free_hdr_from_disk(&freehdr, free);
                if (be16_to_cpu(bests[findex]) != NULLDATAOFF &&
                    be16_to_cpu(bests[findex]) >= length)
                        dbno = freehdr.firstdb + findex;
@@ -1871,7 +1833,7 @@ xfs_dir2_node_addname_int(
                 * Get the freespace block corresponding to the data block
                 * that was just allocated.
                 */
-               fbno = xfs_dir2_db_to_fdb(mp, dbno);
+               fbno = dp->d_ops->db_to_fdb(mp, dbno);
                error = xfs_dir2_free_try_read(tp, dp,
                                               xfs_dir2_db_to_da(mp, fbno),
                                               &fbp);
@@ -1888,12 +1850,12 @@ xfs_dir2_node_addname_int(
                        if (error)
                                return error;
 
-                       if (unlikely(xfs_dir2_db_to_fdb(mp, dbno) != fbno)) {
+                       if (unlikely(dp->d_ops->db_to_fdb(mp, dbno) != fbno)) {
                                xfs_alert(mp,
                        "%s: dir ino %llu needed freesp block %lld for\n"
                        "  data block %lld, got %lld ifbno %llu lastfbno %d",
                                        __func__, (unsigned long long)dp->i_ino,
-                                       (long long)xfs_dir2_db_to_fdb(mp, dbno),
+                                       (long long)dp->d_ops->db_to_fdb(mp, dbno),
                                        (long long)dbno, (long long)fbno,
                                        (unsigned long long)ifbno, lastfbno);
                                if (fblk) {
@@ -1918,30 +1880,30 @@ xfs_dir2_node_addname_int(
                        if (error)
                                return error;
                        free = fbp->b_addr;
-                       bests = xfs_dir3_free_bests_p(mp, free);
-                       xfs_dir3_free_hdr_from_disk(&freehdr, free);
+                       bests = dp->d_ops->free_bests_p(free);
+                       dp->d_ops->free_hdr_from_disk(&freehdr, free);
 
                        /*
                         * Remember the first slot as our empty slot.
                         */
                        freehdr.firstdb = (fbno - XFS_DIR2_FREE_FIRSTDB(mp)) *
-                                       xfs_dir3_free_max_bests(mp);
+                                       dp->d_ops->free_max_bests(mp);
                } else {
                        free = fbp->b_addr;
-                       bests = xfs_dir3_free_bests_p(mp, free);
-                       xfs_dir3_free_hdr_from_disk(&freehdr, free);
+                       bests = dp->d_ops->free_bests_p(free);
+                       dp->d_ops->free_hdr_from_disk(&freehdr, free);
                }
 
                /*
                 * Set the freespace block index from the data block number.
                 */
-               findex = xfs_dir2_db_to_fdindex(mp, dbno);
+               findex = dp->d_ops->db_to_fdindex(mp, dbno);
                /*
                 * If it's after the end of the current entries in the
                 * freespace block, extend that table.
                 */
                if (findex >= freehdr.nvalid) {
-                       ASSERT(findex < xfs_dir3_free_max_bests(mp));
+                       ASSERT(findex < dp->d_ops->free_max_bests(mp));
                        freehdr.nvalid = findex + 1;
                        /*
                         * Tag new entry so nused will go up.
@@ -1954,8 +1916,8 @@ xfs_dir2_node_addname_int(
                 */
                if (bests[findex] == cpu_to_be16(NULLDATAOFF)) {
                        freehdr.nused++;
-                       xfs_dir3_free_hdr_to_disk(fbp->b_addr, &freehdr);
-                       xfs_dir2_free_log_header(tp, fbp);
+                       dp->d_ops->free_hdr_to_disk(fbp->b_addr, &freehdr);
+                       xfs_dir2_free_log_header(tp, dp, fbp);
                }
                /*
                 * Update the real value in the table.
@@ -1963,7 +1925,7 @@ xfs_dir2_node_addname_int(
                 * change again.
                 */
                hdr = dbp->b_addr;
-               bf = xfs_dir3_data_bestfree_p(hdr);
+               bf = dp->d_ops->data_bestfree_p(hdr);
                bests[findex] = bf[0].length;
                logfree = 1;
        }
@@ -1985,7 +1947,7 @@ xfs_dir2_node_addname_int(
                if (error)
                        return error;
                hdr = dbp->b_addr;
-               bf = xfs_dir3_data_bestfree_p(hdr);
+               bf = dp->d_ops->data_bestfree_p(hdr);
                logfree = 0;
        }
        ASSERT(be16_to_cpu(bf[0].length) >= length);
@@ -1998,7 +1960,7 @@ xfs_dir2_node_addname_int(
        /*
         * Mark the first part of the unused space, inuse for us.
         */
-       xfs_dir2_data_use_free(tp, dbp, dup,
+       xfs_dir2_data_use_free(tp, dp, dbp, dup,
                (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
                &needlog, &needscan);
        /*
@@ -2008,24 +1970,24 @@ xfs_dir2_node_addname_int(
        dep->inumber = cpu_to_be64(args->inumber);
        dep->namelen = args->namelen;
        memcpy(dep->name, args->name, dep->namelen);
-       xfs_dir3_dirent_put_ftype(mp, dep, args->filetype);
-       tagp = xfs_dir3_data_entry_tag_p(mp, dep);
+       dp->d_ops->data_put_ftype(dep, args->filetype);
+       tagp = dp->d_ops->data_entry_tag_p(dep);
        *tagp = cpu_to_be16((char *)dep - (char *)hdr);
-       xfs_dir2_data_log_entry(tp, dbp, dep);
+       xfs_dir2_data_log_entry(tp, dp, dbp, dep);
        /*
         * Rescan the block for bestfree if needed.
         */
        if (needscan)
-               xfs_dir2_data_freescan(mp, hdr, &needlog);
+               xfs_dir2_data_freescan(dp, hdr, &needlog);
        /*
         * Log the data block header if needed.
         */
        if (needlog)
-               xfs_dir2_data_log_header(tp, dbp);
+               xfs_dir2_data_log_header(tp, dp, dbp);
        /*
         * If the freespace entry is now wrong, update it.
         */
-       bests = xfs_dir3_free_bests_p(mp, free); /* gcc is so stupid */
+       bests = dp->d_ops->free_bests_p(free); /* gcc is so stupid */
        if (be16_to_cpu(bests[findex]) != be16_to_cpu(bf[0].length)) {
                bests[findex] = bf[0].length;
                logfree = 1;
@@ -2034,7 +1996,7 @@ xfs_dir2_node_addname_int(
         * Log the freespace entry if needed.
         */
        if (logfree)
-               xfs_dir2_free_log_bests(tp, fbp, findex, findex);
+               xfs_dir2_free_log_bests(tp, dp, fbp, findex, findex);
        /*
         * Return the data block and offset in args, then drop the data block.
         */
@@ -2212,7 +2174,7 @@ xfs_dir2_node_replace(
                blk = &state->path.blk[state->path.active - 1];
                ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC);
                leaf = blk->bp->b_addr;
-               ents = xfs_dir3_leaf_ents_p(leaf);
+               ents = args->dp->d_ops->leaf_ents_p(leaf);
                lep = &ents[blk->index];
                ASSERT(state->extravalid);
                /*
@@ -2229,8 +2191,9 @@ xfs_dir2_node_replace(
                 * Fill in the new inode number and log the entry.
                 */
                dep->inumber = cpu_to_be64(inum);
-               xfs_dir3_dirent_put_ftype(state->mp, dep, args->filetype);
-               xfs_dir2_data_log_entry(args->trans, state->extrablk.bp, dep);
+               args->dp->d_ops->data_put_ftype(dep, args->filetype);
+               xfs_dir2_data_log_entry(args->trans, args->dp,
+                                       state->extrablk.bp, dep);
                rval = 0;
        }
        /*
@@ -2285,7 +2248,7 @@ xfs_dir2_node_trim_free(
        if (!bp)
                return 0;
        free = bp->b_addr;
-       xfs_dir3_free_hdr_from_disk(&freehdr, free);
+       dp->d_ops->free_hdr_from_disk(&freehdr, free);
 
        /*
         * If there are used entries, there's nothing to do.
index 1bad84c408295ae4db2de71d96c74b18fff5b2b2..8b9d2281f85b3c6efdec0a6906a1506074871320 100644 (file)
@@ -59,7 +59,8 @@ extern int xfs_dir3_data_readahead(struct xfs_trans *tp, struct xfs_inode *dp,
 
 extern struct xfs_dir2_data_free *
 xfs_dir2_data_freeinsert(struct xfs_dir2_data_hdr *hdr,
-               struct xfs_dir2_data_unused *dup, int *loghead);
+               struct xfs_dir2_data_free *bf, struct xfs_dir2_data_unused *dup,
+               int *loghead);
 extern int xfs_dir3_data_init(struct xfs_da_args *args, xfs_dir2_db_t blkno,
                struct xfs_buf **bpp);
 
@@ -76,9 +77,9 @@ extern void xfs_dir3_leaf_compact_x1(struct xfs_dir3_icleaf_hdr *leafhdr,
                int *lowstalep, int *highstalep, int *lowlogp, int *highlogp);
 extern int xfs_dir3_leaf_get_buf(struct xfs_da_args *args, xfs_dir2_db_t bno,
                struct xfs_buf **bpp, __uint16_t magic);
-extern void xfs_dir3_leaf_log_ents(struct xfs_trans *tp, struct xfs_buf *bp,
-               int first, int last);
-extern void xfs_dir3_leaf_log_header(struct xfs_trans *tp,
+extern void xfs_dir3_leaf_log_ents(struct xfs_trans *tp, struct xfs_inode *dp,
+               struct xfs_buf *bp, int first, int last);
+extern void xfs_dir3_leaf_log_header(struct xfs_trans *tp, struct xfs_inode *dp,
                struct xfs_buf *bp);
 extern int xfs_dir2_leaf_lookup(struct xfs_da_args *args);
 extern int xfs_dir2_leaf_removename(struct xfs_da_args *args);
@@ -93,21 +94,18 @@ xfs_dir3_leaf_find_entry(struct xfs_dir3_icleaf_hdr *leafhdr,
                int lowstale, int highstale, int *lfloglow, int *lfloghigh);
 extern int xfs_dir2_node_to_leaf(struct xfs_da_state *state);
 
-extern void xfs_dir3_leaf_hdr_from_disk(struct xfs_dir3_icleaf_hdr *to,
-               struct xfs_dir2_leaf *from);
-extern void xfs_dir3_leaf_hdr_to_disk(struct xfs_dir2_leaf *to,
-               struct xfs_dir3_icleaf_hdr *from);
-extern bool xfs_dir3_leaf_check_int(struct xfs_mount *mp,
+extern bool xfs_dir3_leaf_check_int(struct xfs_mount *mp, struct xfs_inode *dp,
                struct xfs_dir3_icleaf_hdr *hdr, struct xfs_dir2_leaf *leaf);
 
 /* xfs_dir2_node.c */
 extern int xfs_dir2_leaf_to_node(struct xfs_da_args *args,
                struct xfs_buf *lbp);
-extern xfs_dahash_t xfs_dir2_leafn_lasthash(struct xfs_buf *bp, int *count);
+extern xfs_dahash_t xfs_dir2_leafn_lasthash(struct xfs_inode *dp,
+               struct xfs_buf *bp, int *count);
 extern int xfs_dir2_leafn_lookup_int(struct xfs_buf *bp,
                struct xfs_da_args *args, int *indexp,
                struct xfs_da_state *state);
-extern int xfs_dir2_leafn_order(struct xfs_buf *leaf1_bp,
+extern int xfs_dir2_leafn_order(struct xfs_inode *dp, struct xfs_buf *leaf1_bp,
                struct xfs_buf *leaf2_bp);
 extern int xfs_dir2_leafn_split(struct xfs_da_state *state,
        struct xfs_da_state_blk *oldblk, struct xfs_da_state_blk *newblk);
index 8f84153e98a8c0f52e27aa9a0a1352779e29e61e..c4e50c6ed5845fb94a46c77a0f4f1ae987d4d87f 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_bmap.h"
+#include "xfs_trans.h"
+#include "xfs_dinode.h"
 
 /*
  * Directory file type support functions
@@ -119,9 +119,9 @@ xfs_dir2_sf_getdents(
         * mp->m_dirdatablk.
         */
        dot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
-                                            xfs_dir3_data_dot_offset(mp));
+                                               dp->d_ops->data_dot_offset);
        dotdot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
-                                               xfs_dir3_data_dotdot_offset(mp));
+                                               dp->d_ops->data_dotdot_offset);
 
        /*
         * Put . entry unless we're starting past it.
@@ -136,7 +136,7 @@ xfs_dir2_sf_getdents(
         * Put .. entry unless we're starting past it.
         */
        if (ctx->pos <= dotdot_offset) {
-               ino = xfs_dir2_sf_get_parent_ino(sfp);
+               ino = dp->d_ops->sf_get_parent_ino(sfp);
                ctx->pos = dotdot_offset & 0x7fffffff;
                if (!dir_emit(ctx, "..", 2, ino, DT_DIR))
                        return 0;
@@ -153,17 +153,17 @@ xfs_dir2_sf_getdents(
                                xfs_dir2_sf_get_offset(sfep));
 
                if (ctx->pos > off) {
-                       sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
+                       sfep = dp->d_ops->sf_nextentry(sfp, sfep);
                        continue;
                }
 
-               ino = xfs_dir3_sfe_get_ino(mp, sfp, sfep);
-               filetype = xfs_dir3_sfe_get_ftype(mp, sfp, sfep);
+               ino = dp->d_ops->sf_get_ino(sfp, sfep);
+               filetype = dp->d_ops->sf_get_ftype(sfep);
                ctx->pos = off & 0x7fffffff;
                if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,
                            xfs_dir3_get_dtype(mp, filetype)))
                        return 0;
-               sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
+               sfep = dp->d_ops->sf_nextentry(sfp, sfep);
        }
 
        ctx->pos = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) &
@@ -213,7 +213,7 @@ xfs_dir2_block_getdents(
         * Set up values for the loop.
         */
        btp = xfs_dir2_block_tail_p(mp, hdr);
-       ptr = (char *)xfs_dir3_data_entry_p(hdr);
+       ptr = (char *)dp->d_ops->data_entry_p(hdr);
        endptr = (char *)xfs_dir2_block_leaf_p(btp);
 
        /*
@@ -237,7 +237,7 @@ xfs_dir2_block_getdents(
                /*
                 * Bump pointer for the next iteration.
                 */
-               ptr += xfs_dir3_data_entsize(mp, dep->namelen);
+               ptr += dp->d_ops->data_entsize(dep->namelen);
                /*
                 * The entry is before the desired starting point, skip it.
                 */
@@ -248,7 +248,7 @@ xfs_dir2_block_getdents(
                                            (char *)dep - (char *)hdr);
 
                ctx->pos = cook & 0x7fffffff;
-               filetype = xfs_dir3_dirent_get_ftype(mp, dep);
+               filetype = dp->d_ops->data_get_ftype(dep);
                /*
                 * If it didn't fit, set the final offset to here & return.
                 */
@@ -578,13 +578,13 @@ xfs_dir2_leaf_getdents(
                        /*
                         * Find our position in the block.
                         */
-                       ptr = (char *)xfs_dir3_data_entry_p(hdr);
+                       ptr = (char *)dp->d_ops->data_entry_p(hdr);
                        byteoff = xfs_dir2_byte_to_off(mp, curoff);
                        /*
                         * Skip past the header.
                         */
                        if (byteoff == 0)
-                               curoff += xfs_dir3_data_entry_offset(hdr);
+                               curoff += dp->d_ops->data_entry_offset;
                        /*
                         * Skip past entries until we reach our offset.
                         */
@@ -601,7 +601,7 @@ xfs_dir2_leaf_getdents(
                                        }
                                        dep = (xfs_dir2_data_entry_t *)ptr;
                                        length =
-                                          xfs_dir3_data_entsize(mp, dep->namelen);
+                                          dp->d_ops->data_entsize(dep->namelen);
                                        ptr += length;
                                }
                                /*
@@ -632,8 +632,8 @@ xfs_dir2_leaf_getdents(
                }
 
                dep = (xfs_dir2_data_entry_t *)ptr;
-               length = xfs_dir3_data_entsize(mp, dep->namelen);
-               filetype = xfs_dir3_dirent_get_ftype(mp, dep);
+               length = dp->d_ops->data_entsize(dep->namelen);
+               filetype = dp->d_ops->data_get_ftype(dep);
 
                ctx->pos = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
                if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
index 3ef6d402084ccf9dabc622ee5db22b41170e3e54..aafc6e46cb5803620998399bea5da520aa720f83 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_error.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_trace.h"
+#include "xfs_dinode.h"
 
 /*
  * Prototypes for internal functions.
@@ -56,89 +56,6 @@ static void xfs_dir2_sf_toino4(xfs_da_args_t *args);
 static void xfs_dir2_sf_toino8(xfs_da_args_t *args);
 #endif /* XFS_BIG_INUMS */
 
-/*
- * Inode numbers in short-form directories can come in two versions,
- * either 4 bytes or 8 bytes wide.  These helpers deal with the
- * two forms transparently by looking at the headers i8count field.
- *
- * For 64-bit inode number the most significant byte must be zero.
- */
-static xfs_ino_t
-xfs_dir2_sf_get_ino(
-       struct xfs_dir2_sf_hdr  *hdr,
-       xfs_dir2_inou_t         *from)
-{
-       if (hdr->i8count)
-               return get_unaligned_be64(&from->i8.i) & 0x00ffffffffffffffULL;
-       else
-               return get_unaligned_be32(&from->i4.i);
-}
-
-static void
-xfs_dir2_sf_put_ino(
-       struct xfs_dir2_sf_hdr  *hdr,
-       xfs_dir2_inou_t         *to,
-       xfs_ino_t               ino)
-{
-       ASSERT((ino & 0xff00000000000000ULL) == 0);
-
-       if (hdr->i8count)
-               put_unaligned_be64(ino, &to->i8.i);
-       else
-               put_unaligned_be32(ino, &to->i4.i);
-}
-
-xfs_ino_t
-xfs_dir2_sf_get_parent_ino(
-       struct xfs_dir2_sf_hdr  *hdr)
-{
-       return xfs_dir2_sf_get_ino(hdr, &hdr->parent);
-}
-
-void
-xfs_dir2_sf_put_parent_ino(
-       struct xfs_dir2_sf_hdr  *hdr,
-       xfs_ino_t               ino)
-{
-       xfs_dir2_sf_put_ino(hdr, &hdr->parent, ino);
-}
-
-/*
- * In short-form directory entries the inode numbers are stored at variable
- * offset behind the entry name. If the entry stores a filetype value, then it
- * sits between the name and the inode number. Hence the inode numbers may only
- * be accessed through the helpers below.
- */
-static xfs_dir2_inou_t *
-xfs_dir3_sfe_inop(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_entry *sfep)
-{
-       __uint8_t       *ptr = &sfep->name[sfep->namelen];
-       if (xfs_sb_version_hasftype(&mp->m_sb))
-               ptr++;
-       return (xfs_dir2_inou_t *)ptr;
-}
-
-xfs_ino_t
-xfs_dir3_sfe_get_ino(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_hdr  *hdr,
-       struct xfs_dir2_sf_entry *sfep)
-{
-       return xfs_dir2_sf_get_ino(hdr, xfs_dir3_sfe_inop(mp, sfep));
-}
-
-void
-xfs_dir3_sfe_put_ino(
-       struct xfs_mount        *mp,
-       struct xfs_dir2_sf_hdr  *hdr,
-       struct xfs_dir2_sf_entry *sfep,
-       xfs_ino_t               ino)
-{
-       xfs_dir2_sf_put_ino(hdr, xfs_dir3_sfe_inop(mp, sfep), ino);
-}
-
 /*
  * Given a block directory (dp/block), calculate its size as a shortform (sf)
  * directory and a header for the sf directory, if it will fit it the
@@ -226,7 +143,7 @@ xfs_dir2_block_sfsize(
         */
        sfhp->count = count;
        sfhp->i8count = i8count;
-       xfs_dir2_sf_put_parent_ino(sfhp, parent);
+       dp->d_ops->sf_put_parent_ino(sfhp, parent);
        return size;
 }
 
@@ -293,7 +210,7 @@ xfs_dir2_block_to_sf(
         * Set up to loop over the block's entries.
         */
        btp = xfs_dir2_block_tail_p(mp, hdr);
-       ptr = (char *)xfs_dir3_data_entry_p(hdr);
+       ptr = (char *)dp->d_ops->data_entry_p(hdr);
        endptr = (char *)xfs_dir2_block_leaf_p(btp);
        sfep = xfs_dir2_sf_firstentry(sfp);
        /*
@@ -321,7 +238,7 @@ xfs_dir2_block_to_sf(
                else if (dep->namelen == 2 &&
                         dep->name[0] == '.' && dep->name[1] == '.')
                        ASSERT(be64_to_cpu(dep->inumber) ==
-                              xfs_dir2_sf_get_parent_ino(sfp));
+                              dp->d_ops->sf_get_parent_ino(sfp));
                /*
                 * Normal entry, copy it into shortform.
                 */
@@ -331,14 +248,14 @@ xfs_dir2_block_to_sf(
                                (xfs_dir2_data_aoff_t)
                                ((char *)dep - (char *)hdr));
                        memcpy(sfep->name, dep->name, dep->namelen);
-                       xfs_dir3_sfe_put_ino(mp, sfp, sfep,
-                                            be64_to_cpu(dep->inumber));
-                       xfs_dir3_sfe_put_ftype(mp, sfp, sfep,
-                                       xfs_dir3_dirent_get_ftype(mp, dep));
+                       dp->d_ops->sf_put_ino(sfp, sfep,
+                                             be64_to_cpu(dep->inumber));
+                       dp->d_ops->sf_put_ftype(sfep,
+                                       dp->d_ops->data_get_ftype(dep));
 
-                       sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
+                       sfep = dp->d_ops->sf_nextentry(sfp, sfep);
                }
-               ptr += xfs_dir3_data_entsize(mp, dep->namelen);
+               ptr += dp->d_ops->data_entsize(dep->namelen);
        }
        ASSERT((char *)sfep - (char *)sfp == size);
        xfs_dir2_sf_check(args);
@@ -389,7 +306,7 @@ xfs_dir2_sf_addname(
        /*
         * Compute entry (and change in) size.
         */
-       add_entsize = xfs_dir3_sf_entsize(dp->i_mount, sfp, args->namelen);
+       add_entsize = dp->d_ops->sf_entsize(sfp, args->namelen);
        incr_isize = add_entsize;
        objchange = 0;
 #if XFS_BIG_INUMS
@@ -483,8 +400,7 @@ xfs_dir2_sf_addname_easy(
        /*
         * Grow the in-inode space.
         */
-       xfs_idata_realloc(dp,
-                         xfs_dir3_sf_entsize(dp->i_mount, sfp, args->namelen),
+       xfs_idata_realloc(dp, dp->d_ops->sf_entsize(sfp, args->namelen),
                          XFS_DATA_FORK);
        /*
         * Need to set up again due to realloc of the inode data.
@@ -497,8 +413,8 @@ xfs_dir2_sf_addname_easy(
        sfep->namelen = args->namelen;
        xfs_dir2_sf_put_offset(sfep, offset);
        memcpy(sfep->name, args->name, sfep->namelen);
-       xfs_dir3_sfe_put_ino(dp->i_mount, sfp, sfep, args->inumber);
-       xfs_dir3_sfe_put_ftype(dp->i_mount, sfp, sfep, args->filetype);
+       dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
+       dp->d_ops->sf_put_ftype(sfep, args->filetype);
 
        /*
         * Update the header and inode.
@@ -557,13 +473,13 @@ xfs_dir2_sf_addname_hard(
         * to insert the new entry.
         * If it's going to end up at the end then oldsfep will point there.
         */
-       for (offset = xfs_dir3_data_first_offset(mp),
+       for (offset = dp->d_ops->data_first_offset,
              oldsfep = xfs_dir2_sf_firstentry(oldsfp),
-             add_datasize = xfs_dir3_data_entsize(mp, args->namelen),
+             add_datasize = dp->d_ops->data_entsize(args->namelen),
              eof = (char *)oldsfep == &buf[old_isize];
             !eof;
-            offset = new_offset + xfs_dir3_data_entsize(mp, oldsfep->namelen),
-             oldsfep = xfs_dir3_sf_nextentry(mp, oldsfp, oldsfep),
+            offset = new_offset + dp->d_ops->data_entsize(oldsfep->namelen),
+             oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep),
              eof = (char *)oldsfep == &buf[old_isize]) {
                new_offset = xfs_dir2_sf_get_offset(oldsfep);
                if (offset + add_datasize <= new_offset)
@@ -592,8 +508,8 @@ xfs_dir2_sf_addname_hard(
        sfep->namelen = args->namelen;
        xfs_dir2_sf_put_offset(sfep, offset);
        memcpy(sfep->name, args->name, sfep->namelen);
-       xfs_dir3_sfe_put_ino(mp, sfp, sfep, args->inumber);
-       xfs_dir3_sfe_put_ftype(mp, sfp, sfep, args->filetype);
+       dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
+       dp->d_ops->sf_put_ftype(sfep, args->filetype);
        sfp->count++;
 #if XFS_BIG_INUMS
        if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
@@ -603,7 +519,7 @@ xfs_dir2_sf_addname_hard(
         * If there's more left to copy, do that.
         */
        if (!eof) {
-               sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
+               sfep = dp->d_ops->sf_nextentry(sfp, sfep);
                memcpy(sfep, oldsfep, old_isize - nbytes);
        }
        kmem_free(buf);
@@ -639,8 +555,8 @@ xfs_dir2_sf_addname_pick(
        mp = dp->i_mount;
 
        sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
-       size = xfs_dir3_data_entsize(mp, args->namelen);
-       offset = xfs_dir3_data_first_offset(mp);
+       size = dp->d_ops->data_entsize(args->namelen);
+       offset = dp->d_ops->data_first_offset;
        sfep = xfs_dir2_sf_firstentry(sfp);
        holefit = 0;
        /*
@@ -652,8 +568,8 @@ xfs_dir2_sf_addname_pick(
                if (!holefit)
                        holefit = offset + size <= xfs_dir2_sf_get_offset(sfep);
                offset = xfs_dir2_sf_get_offset(sfep) +
-                        xfs_dir3_data_entsize(mp, sfep->namelen);
-               sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
+                        dp->d_ops->data_entsize(sfep->namelen);
+               sfep = dp->d_ops->sf_nextentry(sfp, sfep);
        }
        /*
         * Calculate data bytes used excluding the new entry, if this
@@ -713,21 +629,20 @@ xfs_dir2_sf_check(
        mp = dp->i_mount;
 
        sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
-       offset = xfs_dir3_data_first_offset(mp);
-       ino = xfs_dir2_sf_get_parent_ino(sfp);
+       offset = dp->d_ops->data_first_offset;
+       ino = dp->d_ops->sf_get_parent_ino(sfp);
        i8count = ino > XFS_DIR2_MAX_SHORT_INUM;
 
        for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
             i < sfp->count;
-            i++, sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep)) {
+            i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
                ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset);
-               ino = xfs_dir3_sfe_get_ino(mp, sfp, sfep);
+               ino = dp->d_ops->sf_get_ino(sfp, sfep);
                i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
                offset =
                        xfs_dir2_sf_get_offset(sfep) +
-                       xfs_dir3_data_entsize(mp, sfep->namelen);
-               ASSERT(xfs_dir3_sfe_get_ftype(mp, sfp, sfep) <
-                                                       XFS_DIR3_FT_MAX);
+                       dp->d_ops->data_entsize(sfep->namelen);
+               ASSERT(dp->d_ops->sf_get_ftype(sfep) < XFS_DIR3_FT_MAX);
        }
        ASSERT(i8count == sfp->i8count);
        ASSERT(XFS_BIG_INUMS || i8count == 0);
@@ -783,7 +698,7 @@ xfs_dir2_sf_create(
        /*
         * Now can put in the inode number, since i8count is set.
         */
-       xfs_dir2_sf_put_parent_ino(sfp, pino);
+       dp->d_ops->sf_put_parent_ino(sfp, pino);
        sfp->count = 0;
        dp->i_d.di_size = size;
        xfs_dir2_sf_check(args);
@@ -838,7 +753,7 @@ xfs_dir2_sf_lookup(
         */
        if (args->namelen == 2 &&
            args->name[0] == '.' && args->name[1] == '.') {
-               args->inumber = xfs_dir2_sf_get_parent_ino(sfp);
+               args->inumber = dp->d_ops->sf_get_parent_ino(sfp);
                args->cmpresult = XFS_CMP_EXACT;
                args->filetype = XFS_DIR3_FT_DIR;
                return XFS_ERROR(EEXIST);
@@ -848,7 +763,7 @@ xfs_dir2_sf_lookup(
         */
        ci_sfep = NULL;
        for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
-            i++, sfep = xfs_dir3_sf_nextentry(dp->i_mount, sfp, sfep)) {
+            i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
                /*
                 * Compare name and if it's an exact match, return the inode
                 * number. If it's the first case-insensitive match, store the
@@ -858,10 +773,8 @@ xfs_dir2_sf_lookup(
                                                                sfep->namelen);
                if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
                        args->cmpresult = cmp;
-                       args->inumber = xfs_dir3_sfe_get_ino(dp->i_mount,
-                                                            sfp, sfep);
-                       args->filetype = xfs_dir3_sfe_get_ftype(dp->i_mount,
-                                                               sfp, sfep);
+                       args->inumber = dp->d_ops->sf_get_ino(sfp, sfep);
+                       args->filetype = dp->d_ops->sf_get_ftype(sfep);
                        if (cmp == XFS_CMP_EXACT)
                                return XFS_ERROR(EEXIST);
                        ci_sfep = sfep;
@@ -917,10 +830,10 @@ xfs_dir2_sf_removename(
         * Find the one we're deleting.
         */
        for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
-            i++, sfep = xfs_dir3_sf_nextentry(dp->i_mount, sfp, sfep)) {
+            i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
                if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
                                                                XFS_CMP_EXACT) {
-                       ASSERT(xfs_dir3_sfe_get_ino(dp->i_mount, sfp, sfep) ==
+                       ASSERT(dp->d_ops->sf_get_ino(sfp, sfep) ==
                               args->inumber);
                        break;
                }
@@ -934,7 +847,7 @@ xfs_dir2_sf_removename(
         * Calculate sizes.
         */
        byteoff = (int)((char *)sfep - (char *)sfp);
-       entsize = xfs_dir3_sf_entsize(dp->i_mount, sfp, args->namelen);
+       entsize = dp->d_ops->sf_entsize(sfp, args->namelen);
        newsize = oldsize - entsize;
        /*
         * Copy the part if any after the removed entry, sliding it down.
@@ -1041,28 +954,25 @@ xfs_dir2_sf_replace(
        if (args->namelen == 2 &&
            args->name[0] == '.' && args->name[1] == '.') {
 #if XFS_BIG_INUMS || defined(DEBUG)
-               ino = xfs_dir2_sf_get_parent_ino(sfp);
+               ino = dp->d_ops->sf_get_parent_ino(sfp);
                ASSERT(args->inumber != ino);
 #endif
-               xfs_dir2_sf_put_parent_ino(sfp, args->inumber);
+               dp->d_ops->sf_put_parent_ino(sfp, args->inumber);
        }
        /*
         * Normal entry, look for the name.
         */
        else {
                for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
-                    i++, sfep = xfs_dir3_sf_nextentry(dp->i_mount, sfp, sfep)) {
+                    i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) {
                        if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
                                                                XFS_CMP_EXACT) {
 #if XFS_BIG_INUMS || defined(DEBUG)
-                               ino = xfs_dir3_sfe_get_ino(dp->i_mount,
-                                                          sfp, sfep);
+                               ino = dp->d_ops->sf_get_ino(sfp, sfep);
                                ASSERT(args->inumber != ino);
 #endif
-                               xfs_dir3_sfe_put_ino(dp->i_mount, sfp, sfep,
-                                                    args->inumber);
-                               xfs_dir3_sfe_put_ftype(dp->i_mount, sfp, sfep,
-                                                      args->filetype);
+                               dp->d_ops->sf_put_ino(sfp, sfep, args->inumber);
+                               dp->d_ops->sf_put_ftype(sfep, args->filetype);
                                break;
                        }
                }
@@ -1165,22 +1075,21 @@ xfs_dir2_sf_toino4(
         */
        sfp->count = oldsfp->count;
        sfp->i8count = 0;
-       xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp));
+       dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp));
        /*
         * Copy the entries field by field.
         */
        for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
                    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
             i < sfp->count;
-            i++, sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep),
-                 oldsfep = xfs_dir3_sf_nextentry(mp, oldsfp, oldsfep)) {
+            i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep),
+                 oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) {
                sfep->namelen = oldsfep->namelen;
                sfep->offset = oldsfep->offset;
                memcpy(sfep->name, oldsfep->name, sfep->namelen);
-               xfs_dir3_sfe_put_ino(mp, sfp, sfep,
-                       xfs_dir3_sfe_get_ino(mp, oldsfp, oldsfep));
-               xfs_dir3_sfe_put_ftype(mp, sfp, sfep,
-                       xfs_dir3_sfe_get_ftype(mp, oldsfp, oldsfep));
+               dp->d_ops->sf_put_ino(sfp, sfep,
+                                     dp->d_ops->sf_get_ino(oldsfp, oldsfep));
+               dp->d_ops->sf_put_ftype(sfep, dp->d_ops->sf_get_ftype(oldsfep));
        }
        /*
         * Clean up the inode.
@@ -1244,22 +1153,21 @@ xfs_dir2_sf_toino8(
         */
        sfp->count = oldsfp->count;
        sfp->i8count = 1;
-       xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp));
+       dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp));
        /*
         * Copy the entries field by field.
         */
        for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
                    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
             i < sfp->count;
-            i++, sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep),
-                 oldsfep = xfs_dir3_sf_nextentry(mp, oldsfp, oldsfep)) {
+            i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep),
+                 oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) {
                sfep->namelen = oldsfep->namelen;
                sfep->offset = oldsfep->offset;
                memcpy(sfep->name, oldsfep->name, sfep->namelen);
-               xfs_dir3_sfe_put_ino(mp, sfp, sfep,
-                       xfs_dir3_sfe_get_ino(mp, oldsfp, oldsfep));
-               xfs_dir3_sfe_put_ftype(mp, sfp, sfep,
-                       xfs_dir3_sfe_get_ftype(mp, oldsfp, oldsfep));
+               dp->d_ops->sf_put_ino(sfp, sfep,
+                                     dp->d_ops->sf_get_ino(oldsfp, oldsfep));
+               dp->d_ops->sf_put_ftype(sfep, dp->d_ops->sf_get_ftype(oldsfep));
        }
        /*
         * Clean up the inode.
index 45560ee1a4ba8b1ccfdc36f9616cc5355e03558d..8367d6dc18c9df7f51cd565e11395cba24929a53 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
 #include "xfs_quota.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_btree.h"
 #include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_alloc_btree.h"
 #include "xfs_alloc.h"
 #include "xfs_error.h"
 #include "xfs_extent_busy.h"
 #include "xfs_discard.h"
 #include "xfs_trace.h"
+#include "xfs_log.h"
 
 STATIC int
 xfs_trim_extents(
index 1ee776d477c3d1b240378993f946e9716f62c83d..6b1e695caf0ebf8bf256150d2efc9483667b837d 100644 (file)
 #include "xfs.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
-#include "xfs_rtalloc.h"
+#include "xfs_alloc.h"
+#include "xfs_quota.h"
 #include "xfs_error.h"
-#include "xfs_itable.h"
-#include "xfs_attr.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_trans_space.h"
 #include "xfs_trans_priv.h"
 #include "xfs_qm.h"
 #include "xfs_cksum.h"
 #include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_bmap_btree.h"
 
 /*
  * Lock order:
@@ -292,118 +292,6 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp)
        dqp->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5;
 }
 
-STATIC bool
-xfs_dquot_buf_verify_crc(
-       struct xfs_mount        *mp,
-       struct xfs_buf          *bp)
-{
-       struct xfs_dqblk        *d = (struct xfs_dqblk *)bp->b_addr;
-       int                     ndquots;
-       int                     i;
-
-       if (!xfs_sb_version_hascrc(&mp->m_sb))
-               return true;
-
-       /*
-        * if we are in log recovery, the quota subsystem has not been
-        * initialised so we have no quotainfo structure. In that case, we need
-        * to manually calculate the number of dquots in the buffer.
-        */
-       if (mp->m_quotainfo)
-               ndquots = mp->m_quotainfo->qi_dqperchunk;
-       else
-               ndquots = xfs_qm_calc_dquots_per_chunk(mp,
-                                       XFS_BB_TO_FSB(mp, bp->b_length));
-
-       for (i = 0; i < ndquots; i++, d++) {
-               if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
-                                XFS_DQUOT_CRC_OFF))
-                       return false;
-               if (!uuid_equal(&d->dd_uuid, &mp->m_sb.sb_uuid))
-                       return false;
-       }
-       return true;
-}
-
-STATIC bool
-xfs_dquot_buf_verify(
-       struct xfs_mount        *mp,
-       struct xfs_buf          *bp)
-{
-       struct xfs_dqblk        *d = (struct xfs_dqblk *)bp->b_addr;
-       xfs_dqid_t              id = 0;
-       int                     ndquots;
-       int                     i;
-
-       /*
-        * if we are in log recovery, the quota subsystem has not been
-        * initialised so we have no quotainfo structure. In that case, we need
-        * to manually calculate the number of dquots in the buffer.
-        */
-       if (mp->m_quotainfo)
-               ndquots = mp->m_quotainfo->qi_dqperchunk;
-       else
-               ndquots = xfs_qm_calc_dquots_per_chunk(mp, bp->b_length);
-
-       /*
-        * On the first read of the buffer, verify that each dquot is valid.
-        * We don't know what the id of the dquot is supposed to be, just that
-        * they should be increasing monotonically within the buffer. If the
-        * first id is corrupt, then it will fail on the second dquot in the
-        * buffer so corruptions could point to the wrong dquot in this case.
-        */
-       for (i = 0; i < ndquots; i++) {
-               struct xfs_disk_dquot   *ddq;
-               int                     error;
-
-               ddq = &d[i].dd_diskdq;
-
-               if (i == 0)
-                       id = be32_to_cpu(ddq->d_id);
-
-               error = xfs_qm_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
-                                      "xfs_dquot_buf_verify");
-               if (error)
-                       return false;
-       }
-       return true;
-}
-
-static void
-xfs_dquot_buf_read_verify(
-       struct xfs_buf  *bp)
-{
-       struct xfs_mount        *mp = bp->b_target->bt_mount;
-
-       if (!xfs_dquot_buf_verify_crc(mp, bp) || !xfs_dquot_buf_verify(mp, bp)) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
-       }
-}
-
-/*
- * we don't calculate the CRC here as that is done when the dquot is flushed to
- * the buffer after the update is done. This ensures that the dquot in the
- * buffer always has an up-to-date CRC value.
- */
-void
-xfs_dquot_buf_write_verify(
-       struct xfs_buf  *bp)
-{
-       struct xfs_mount        *mp = bp->b_target->bt_mount;
-
-       if (!xfs_dquot_buf_verify(mp, bp)) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
-               return;
-       }
-}
-
-const struct xfs_buf_ops xfs_dquot_buf_ops = {
-       .verify_read = xfs_dquot_buf_read_verify,
-       .verify_write = xfs_dquot_buf_write_verify,
-};
-
 /*
  * Allocate a block and fill it with dquots.
  * This is called when the bmapi finds a hole.
@@ -514,6 +402,7 @@ xfs_qm_dqalloc(
 
        return (error);
 }
+
 STATIC int
 xfs_qm_dqrepair(
        struct xfs_mount        *mp,
@@ -547,7 +436,7 @@ xfs_qm_dqrepair(
        /* Do the actual repair of dquots in this buffer */
        for (i = 0; i < mp->m_quotainfo->qi_dqperchunk; i++) {
                ddq = &d[i].dd_diskdq;
-               error = xfs_qm_dqcheck(mp, ddq, firstid + i,
+               error = xfs_dqcheck(mp, ddq, firstid + i,
                                       dqp->dq_flags & XFS_DQ_ALLTYPES,
                                       XFS_QMOPT_DQREPAIR, "xfs_qm_dqrepair");
                if (error) {
@@ -1133,7 +1022,7 @@ xfs_qm_dqflush(
        /*
         * A simple sanity check in case we got a corrupted dquot..
         */
-       error = xfs_qm_dqcheck(mp, &dqp->q_core, be32_to_cpu(ddqp->d_id), 0,
+       error = xfs_dqcheck(mp, &dqp->q_core, be32_to_cpu(ddqp->d_id), 0,
                           XFS_QMOPT_DOWARN, "dqflush (incore copy)");
        if (error) {
                xfs_buf_relse(bp);
index 55abbca2883d84231e7ca61a722f2b394b41376d..d22ed0053c32ea0dd687f7e2ab439b8181d4cbc8 100644 (file)
@@ -172,6 +172,4 @@ static inline struct xfs_dquot *xfs_qm_dqhold(struct xfs_dquot *dqp)
        return dqp;
 }
 
-extern const struct xfs_buf_ops xfs_dquot_buf_ops;
-
 #endif /* __XFS_DQUOT_H__ */
diff --git a/fs/xfs/xfs_dquot_buf.c b/fs/xfs/xfs_dquot_buf.c
new file mode 100644 (file)
index 0000000..d401457
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_quota.h"
+#include "xfs_trans.h"
+#include "xfs_qm.h"
+#include "xfs_error.h"
+#include "xfs_cksum.h"
+#include "xfs_trace.h"
+
+int
+xfs_calc_dquots_per_chunk(
+       struct xfs_mount        *mp,
+       unsigned int            nbblks) /* basic block units */
+{
+       unsigned int    ndquots;
+
+       ASSERT(nbblks > 0);
+       ndquots = BBTOB(nbblks);
+       do_div(ndquots, sizeof(xfs_dqblk_t));
+
+       return ndquots;
+}
+
+/*
+ * Do some primitive error checking on ondisk dquot data structures.
+ */
+int
+xfs_dqcheck(
+       struct xfs_mount *mp,
+       xfs_disk_dquot_t *ddq,
+       xfs_dqid_t       id,
+       uint             type,    /* used only when IO_dorepair is true */
+       uint             flags,
+       char             *str)
+{
+       xfs_dqblk_t      *d = (xfs_dqblk_t *)ddq;
+       int             errs = 0;
+
+       /*
+        * We can encounter an uninitialized dquot buffer for 2 reasons:
+        * 1. If we crash while deleting the quotainode(s), and those blks got
+        *    used for user data. This is because we take the path of regular
+        *    file deletion; however, the size field of quotainodes is never
+        *    updated, so all the tricks that we play in itruncate_finish
+        *    don't quite matter.
+        *
+        * 2. We don't play the quota buffers when there's a quotaoff logitem.
+        *    But the allocation will be replayed so we'll end up with an
+        *    uninitialized quota block.
+        *
+        * This is all fine; things are still consistent, and we haven't lost
+        * any quota information. Just don't complain about bad dquot blks.
+        */
+       if (ddq->d_magic != cpu_to_be16(XFS_DQUOT_MAGIC)) {
+               if (flags & XFS_QMOPT_DOWARN)
+                       xfs_alert(mp,
+                       "%s : XFS dquot ID 0x%x, magic 0x%x != 0x%x",
+                       str, id, be16_to_cpu(ddq->d_magic), XFS_DQUOT_MAGIC);
+               errs++;
+       }
+       if (ddq->d_version != XFS_DQUOT_VERSION) {
+               if (flags & XFS_QMOPT_DOWARN)
+                       xfs_alert(mp,
+                       "%s : XFS dquot ID 0x%x, version 0x%x != 0x%x",
+                       str, id, ddq->d_version, XFS_DQUOT_VERSION);
+               errs++;
+       }
+
+       if (ddq->d_flags != XFS_DQ_USER &&
+           ddq->d_flags != XFS_DQ_PROJ &&
+           ddq->d_flags != XFS_DQ_GROUP) {
+               if (flags & XFS_QMOPT_DOWARN)
+                       xfs_alert(mp,
+                       "%s : XFS dquot ID 0x%x, unknown flags 0x%x",
+                       str, id, ddq->d_flags);
+               errs++;
+       }
+
+       if (id != -1 && id != be32_to_cpu(ddq->d_id)) {
+               if (flags & XFS_QMOPT_DOWARN)
+                       xfs_alert(mp,
+                       "%s : ondisk-dquot 0x%p, ID mismatch: "
+                       "0x%x expected, found id 0x%x",
+                       str, ddq, id, be32_to_cpu(ddq->d_id));
+               errs++;
+       }
+
+       if (!errs && ddq->d_id) {
+               if (ddq->d_blk_softlimit &&
+                   be64_to_cpu(ddq->d_bcount) >
+                               be64_to_cpu(ddq->d_blk_softlimit)) {
+                       if (!ddq->d_btimer) {
+                               if (flags & XFS_QMOPT_DOWARN)
+                                       xfs_alert(mp,
+                       "%s : Dquot ID 0x%x (0x%p) BLK TIMER NOT STARTED",
+                                       str, (int)be32_to_cpu(ddq->d_id), ddq);
+                               errs++;
+                       }
+               }
+               if (ddq->d_ino_softlimit &&
+                   be64_to_cpu(ddq->d_icount) >
+                               be64_to_cpu(ddq->d_ino_softlimit)) {
+                       if (!ddq->d_itimer) {
+                               if (flags & XFS_QMOPT_DOWARN)
+                                       xfs_alert(mp,
+                       "%s : Dquot ID 0x%x (0x%p) INODE TIMER NOT STARTED",
+                                       str, (int)be32_to_cpu(ddq->d_id), ddq);
+                               errs++;
+                       }
+               }
+               if (ddq->d_rtb_softlimit &&
+                   be64_to_cpu(ddq->d_rtbcount) >
+                               be64_to_cpu(ddq->d_rtb_softlimit)) {
+                       if (!ddq->d_rtbtimer) {
+                               if (flags & XFS_QMOPT_DOWARN)
+                                       xfs_alert(mp,
+                       "%s : Dquot ID 0x%x (0x%p) RTBLK TIMER NOT STARTED",
+                                       str, (int)be32_to_cpu(ddq->d_id), ddq);
+                               errs++;
+                       }
+               }
+       }
+
+       if (!errs || !(flags & XFS_QMOPT_DQREPAIR))
+               return errs;
+
+       if (flags & XFS_QMOPT_DOWARN)
+               xfs_notice(mp, "Re-initializing dquot ID 0x%x", id);
+
+       /*
+        * Typically, a repair is only requested by quotacheck.
+        */
+       ASSERT(id != -1);
+       ASSERT(flags & XFS_QMOPT_DQREPAIR);
+       memset(d, 0, sizeof(xfs_dqblk_t));
+
+       d->dd_diskdq.d_magic = cpu_to_be16(XFS_DQUOT_MAGIC);
+       d->dd_diskdq.d_version = XFS_DQUOT_VERSION;
+       d->dd_diskdq.d_flags = type;
+       d->dd_diskdq.d_id = cpu_to_be32(id);
+
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               uuid_copy(&d->dd_uuid, &mp->m_sb.sb_uuid);
+               xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk),
+                                XFS_DQUOT_CRC_OFF);
+       }
+
+       return errs;
+}
+
+STATIC bool
+xfs_dquot_buf_verify_crc(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp)
+{
+       struct xfs_dqblk        *d = (struct xfs_dqblk *)bp->b_addr;
+       int                     ndquots;
+       int                     i;
+
+       if (!xfs_sb_version_hascrc(&mp->m_sb))
+               return true;
+
+       /*
+        * if we are in log recovery, the quota subsystem has not been
+        * initialised so we have no quotainfo structure. In that case, we need
+        * to manually calculate the number of dquots in the buffer.
+        */
+       if (mp->m_quotainfo)
+               ndquots = mp->m_quotainfo->qi_dqperchunk;
+       else
+               ndquots = xfs_calc_dquots_per_chunk(mp,
+                                       XFS_BB_TO_FSB(mp, bp->b_length));
+
+       for (i = 0; i < ndquots; i++, d++) {
+               if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
+                                XFS_DQUOT_CRC_OFF))
+                       return false;
+               if (!uuid_equal(&d->dd_uuid, &mp->m_sb.sb_uuid))
+                       return false;
+       }
+       return true;
+}
+
+STATIC bool
+xfs_dquot_buf_verify(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp)
+{
+       struct xfs_dqblk        *d = (struct xfs_dqblk *)bp->b_addr;
+       xfs_dqid_t              id = 0;
+       int                     ndquots;
+       int                     i;
+
+       /*
+        * if we are in log recovery, the quota subsystem has not been
+        * initialised so we have no quotainfo structure. In that case, we need
+        * to manually calculate the number of dquots in the buffer.
+        */
+       if (mp->m_quotainfo)
+               ndquots = mp->m_quotainfo->qi_dqperchunk;
+       else
+               ndquots = xfs_calc_dquots_per_chunk(mp, bp->b_length);
+
+       /*
+        * On the first read of the buffer, verify that each dquot is valid.
+        * We don't know what the id of the dquot is supposed to be, just that
+        * they should be increasing monotonically within the buffer. If the
+        * first id is corrupt, then it will fail on the second dquot in the
+        * buffer so corruptions could point to the wrong dquot in this case.
+        */
+       for (i = 0; i < ndquots; i++) {
+               struct xfs_disk_dquot   *ddq;
+               int                     error;
+
+               ddq = &d[i].dd_diskdq;
+
+               if (i == 0)
+                       id = be32_to_cpu(ddq->d_id);
+
+               error = xfs_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
+                                      "xfs_dquot_buf_verify");
+               if (error)
+                       return false;
+       }
+       return true;
+}
+
+static void
+xfs_dquot_buf_read_verify(
+       struct xfs_buf  *bp)
+{
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+
+       if (!xfs_dquot_buf_verify_crc(mp, bp) || !xfs_dquot_buf_verify(mp, bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
+}
+
+/*
+ * we don't calculate the CRC here as that is done when the dquot is flushed to
+ * the buffer after the update is done. This ensures that the dquot in the
+ * buffer always has an up-to-date CRC value.
+ */
+static void
+xfs_dquot_buf_write_verify(
+       struct xfs_buf  *bp)
+{
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+
+       if (!xfs_dquot_buf_verify(mp, bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+               return;
+       }
+}
+
+const struct xfs_buf_ops xfs_dquot_buf_ops = {
+       .verify_read = xfs_dquot_buf_read_verify,
+       .verify_write = xfs_dquot_buf_write_verify,
+};
+
index e838d84b4e85697917c8623418c06ae28140a8b7..92e5f62eefc6612fb5890631edd42629af1e768b 100644 (file)
 #include "xfs.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
-#include "xfs_bmap.h"
-#include "xfs_rtalloc.h"
+#include "xfs_quota.h"
 #include "xfs_error.h"
-#include "xfs_itable.h"
-#include "xfs_attr.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_trans_priv.h"
 #include "xfs_qm.h"
+#include "xfs_log.h"
 
 static inline struct xfs_dq_logitem *DQUOT_ITEM(struct xfs_log_item *lip)
 {
index 1123d93ff79546efe3a9d962460ab1e44e2e1bac..9995b807d627eb564da0747124359caa6141ee57 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
+#include "xfs_format.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
 #include "xfs_error.h"
 
 #ifdef DEBUG
@@ -159,7 +156,7 @@ xfs_error_report(
 {
        if (level <= xfs_error_level) {
                xfs_alert_tag(mp, XFS_PTAG_ERROR_REPORT,
-               "Internal error %s at line %d of file %s.  Caller 0x%p\n",
+               "Internal error %s at line %d of file %s.  Caller 0x%p",
                            tag, linenum, filename, ra);
 
                xfs_stack_trace();
index 066df425c14ffca5b4dacb20f7b3c6fcdd139acb..1399e187d425dc7af0f8b5062afaf312fe5a0927 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
+#include "xfs_da_format.h"
 #include "xfs_dir2.h"
 #include "xfs_export.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
+#include "xfs_log.h"
 
 /*
  * Note that we only accept fileids which are long enough rather than allow
index e43708e2f0806d4daae241ee32e18abcac1a017f..fd22f69049d49861ff1f853ec11a2d23e3eef03f 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_alloc.h"
-#include "xfs_inode.h"
 #include "xfs_extent_busy.h"
 #include "xfs_trace.h"
+#include "xfs_trans.h"
+#include "xfs_log.h"
 
 void
 xfs_extent_busy_insert(
index 985412d65ba5119519300a5b414d7d2ea8ba1707..bfff284d2dcce434a16c72df62713098a812dfd4 100644 (file)
 #ifndef __XFS_EXTENT_BUSY_H__
 #define        __XFS_EXTENT_BUSY_H__
 
+struct xfs_mount;
+struct xfs_trans;
+struct xfs_alloc_arg;
+
 /*
  * Busy block/extent entry.  Indexed by a rbtree in perag to mark blocks that
  * have been freed but whose transactions aren't committed to disk yet.
index dc53e8febbbeaa54812b4e72dc25938718da69c1..3680d04f973fa1c9de4390079a5c5ba3d8e69eae 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_buf_item.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
 #include "xfs_extfree_item.h"
 
 
index 4c749ab543d0de17646993a282f925ebd0314ccf..52c91e1437255c60d1f56041d4eceea4601f6265 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_log.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_trans.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_error.h"
-#include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_ioctl.h"
 #include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_dinode.h"
 
 #include <linux/aio.h>
 #include <linux/dcache.h>
@@ -805,44 +807,64 @@ out:
 
 STATIC long
 xfs_file_fallocate(
-       struct file     *file,
-       int             mode,
-       loff_t          offset,
-       loff_t          len)
+       struct file             *file,
+       int                     mode,
+       loff_t                  offset,
+       loff_t                  len)
 {
-       struct inode    *inode = file_inode(file);
-       long            error;
-       loff_t          new_size = 0;
-       xfs_flock64_t   bf;
-       xfs_inode_t     *ip = XFS_I(inode);
-       int             cmd = XFS_IOC_RESVSP;
-       int             attr_flags = XFS_ATTR_NOLOCK;
+       struct inode            *inode = file_inode(file);
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_trans        *tp;
+       long                    error;
+       loff_t                  new_size = 0;
 
+       if (!S_ISREG(inode->i_mode))
+               return -EINVAL;
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
                return -EOPNOTSUPP;
 
-       bf.l_whence = 0;
-       bf.l_start = offset;
-       bf.l_len = len;
-
        xfs_ilock(ip, XFS_IOLOCK_EXCL);
+       if (mode & FALLOC_FL_PUNCH_HOLE) {
+               error = xfs_free_file_space(ip, offset, len);
+               if (error)
+                       goto out_unlock;
+       } else {
+               if (!(mode & FALLOC_FL_KEEP_SIZE) &&
+                   offset + len > i_size_read(inode)) {
+                       new_size = offset + len;
+                       error = -inode_newsize_ok(inode, new_size);
+                       if (error)
+                               goto out_unlock;
+               }
 
-       if (mode & FALLOC_FL_PUNCH_HOLE)
-               cmd = XFS_IOC_UNRESVSP;
-
-       /* check the new inode size is valid before allocating */
-       if (!(mode & FALLOC_FL_KEEP_SIZE) &&
-           offset + len > i_size_read(inode)) {
-               new_size = offset + len;
-               error = inode_newsize_ok(inode, new_size);
+               error = xfs_alloc_file_space(ip, offset, len,
+                                            XFS_BMAPI_PREALLOC);
                if (error)
                        goto out_unlock;
        }
 
-       if (file->f_flags & O_DSYNC)
-               attr_flags |= XFS_ATTR_SYNC;
+       tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_WRITEID);
+       error = xfs_trans_reserve(tp, &M_RES(ip->i_mount)->tr_writeid, 0, 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               goto out_unlock;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+       ip->i_d.di_mode &= ~S_ISUID;
+       if (ip->i_d.di_mode & S_IXGRP)
+               ip->i_d.di_mode &= ~S_ISGID;
+
+       if (!(mode & FALLOC_FL_PUNCH_HOLE))
+               ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
 
-       error = -xfs_change_file_space(ip, cmd, &bf, 0, attr_flags);
+       xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+       if (file->f_flags & O_DSYNC)
+               xfs_trans_set_sync(tp);
+       error = xfs_trans_commit(tp, 0);
        if (error)
                goto out_unlock;
 
@@ -852,12 +874,12 @@ xfs_file_fallocate(
 
                iattr.ia_valid = ATTR_SIZE;
                iattr.ia_size = new_size;
-               error = -xfs_setattr_size(ip, &iattr, XFS_ATTR_NOLOCK);
+               error = xfs_setattr_size(ip, &iattr);
        }
 
 out_unlock:
        xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-       return error;
+       return -error;
 }
 
 
index ce78e654d37b73693aa4c637e021dda9154ad5d6..12b6e7701985378e56f619dfd58f79be0d45c94c 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
-#include "xfs_log.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_inum.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_ag.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_mount.h"
+#include "xfs_inum.h"
+#include "xfs_inode.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_alloc.h"
 #include "xfs_mru_cache.h"
+#include "xfs_dinode.h"
 #include "xfs_filestream.h"
 #include "xfs_trace.h"
 
index 35c08ff54ca079dcf6c718d6b2d6b75bf24f1721..b6ab5a3cfa125d2204d19760dccac917d0ad957f 100644 (file)
@@ -156,14 +156,259 @@ struct xfs_dsymlink_hdr {
        ((bufsize) - (xfs_sb_version_hascrc(&(mp)->m_sb) ? \
                        sizeof(struct xfs_dsymlink_hdr) : 0))
 
-int xfs_symlink_blocks(struct xfs_mount *mp, int pathlen);
-int xfs_symlink_hdr_set(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset,
-                       uint32_t size, struct xfs_buf *bp);
-bool xfs_symlink_hdr_ok(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset,
-                       uint32_t size, struct xfs_buf *bp);
-void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
-                                struct xfs_inode *ip, struct xfs_ifork *ifp);
-
-extern const struct xfs_buf_ops xfs_symlink_buf_ops;
+
+/*
+ * Allocation Btree format definitions
+ *
+ * There are two on-disk btrees, one sorted by blockno and one sorted
+ * by blockcount and blockno.  All blocks look the same to make the code
+ * simpler; if we have time later, we'll make the optimizations.
+ */
+#define        XFS_ABTB_MAGIC          0x41425442      /* 'ABTB' for bno tree */
+#define        XFS_ABTB_CRC_MAGIC      0x41423342      /* 'AB3B' */
+#define        XFS_ABTC_MAGIC          0x41425443      /* 'ABTC' for cnt tree */
+#define        XFS_ABTC_CRC_MAGIC      0x41423343      /* 'AB3C' */
+
+/*
+ * Data record/key structure
+ */
+typedef struct xfs_alloc_rec {
+       __be32          ar_startblock;  /* starting block number */
+       __be32          ar_blockcount;  /* count of free blocks */
+} xfs_alloc_rec_t, xfs_alloc_key_t;
+
+typedef struct xfs_alloc_rec_incore {
+       xfs_agblock_t   ar_startblock;  /* starting block number */
+       xfs_extlen_t    ar_blockcount;  /* count of free blocks */
+} xfs_alloc_rec_incore_t;
+
+/* btree pointer type */
+typedef __be32 xfs_alloc_ptr_t;
+
+/*
+ * Block numbers in the AG:
+ * SB is sector 0, AGF is sector 1, AGI is sector 2, AGFL is sector 3.
+ */
+#define        XFS_BNO_BLOCK(mp)       ((xfs_agblock_t)(XFS_AGFL_BLOCK(mp) + 1))
+#define        XFS_CNT_BLOCK(mp)       ((xfs_agblock_t)(XFS_BNO_BLOCK(mp) + 1))
+
+
+/*
+ * Inode Allocation Btree format definitions
+ *
+ * There is a btree for the inode map per allocation group.
+ */
+#define        XFS_IBT_MAGIC           0x49414254      /* 'IABT' */
+#define        XFS_IBT_CRC_MAGIC       0x49414233      /* 'IAB3' */
+
+typedef        __uint64_t      xfs_inofree_t;
+#define        XFS_INODES_PER_CHUNK            (NBBY * sizeof(xfs_inofree_t))
+#define        XFS_INODES_PER_CHUNK_LOG        (XFS_NBBYLOG + 3)
+#define        XFS_INOBT_ALL_FREE              ((xfs_inofree_t)-1)
+#define        XFS_INOBT_MASK(i)               ((xfs_inofree_t)1 << (i))
+
+static inline xfs_inofree_t xfs_inobt_maskn(int i, int n)
+{
+       return ((n >= XFS_INODES_PER_CHUNK ? 0 : XFS_INOBT_MASK(n)) - 1) << i;
+}
+
+/*
+ * Data record structure
+ */
+typedef struct xfs_inobt_rec {
+       __be32          ir_startino;    /* starting inode number */
+       __be32          ir_freecount;   /* count of free inodes (set bits) */
+       __be64          ir_free;        /* free inode mask */
+} xfs_inobt_rec_t;
+
+typedef struct xfs_inobt_rec_incore {
+       xfs_agino_t     ir_startino;    /* starting inode number */
+       __int32_t       ir_freecount;   /* count of free inodes (set bits) */
+       xfs_inofree_t   ir_free;        /* free inode mask */
+} xfs_inobt_rec_incore_t;
+
+
+/*
+ * Key structure
+ */
+typedef struct xfs_inobt_key {
+       __be32          ir_startino;    /* starting inode number */
+} xfs_inobt_key_t;
+
+/* btree pointer type */
+typedef __be32 xfs_inobt_ptr_t;
+
+/*
+ * block numbers in the AG.
+ */
+#define        XFS_IBT_BLOCK(mp)               ((xfs_agblock_t)(XFS_CNT_BLOCK(mp) + 1))
+#define        XFS_PREALLOC_BLOCKS(mp)         ((xfs_agblock_t)(XFS_IBT_BLOCK(mp) + 1))
+
+
+
+/*
+ * BMAP Btree format definitions
+ *
+ * This includes both the root block definition that sits inside an inode fork
+ * and the record/pointer formats for the leaf/node in the blocks.
+ */
+#define XFS_BMAP_MAGIC         0x424d4150      /* 'BMAP' */
+#define XFS_BMAP_CRC_MAGIC     0x424d4133      /* 'BMA3' */
+
+/*
+ * Bmap root header, on-disk form only.
+ */
+typedef struct xfs_bmdr_block {
+       __be16          bb_level;       /* 0 is a leaf */
+       __be16          bb_numrecs;     /* current # of data records */
+} xfs_bmdr_block_t;
+
+/*
+ * Bmap btree record and extent descriptor.
+ *  l0:63 is an extent flag (value 1 indicates non-normal).
+ *  l0:9-62 are startoff.
+ *  l0:0-8 and l1:21-63 are startblock.
+ *  l1:0-20 are blockcount.
+ */
+#define BMBT_EXNTFLAG_BITLEN   1
+#define BMBT_STARTOFF_BITLEN   54
+#define BMBT_STARTBLOCK_BITLEN 52
+#define BMBT_BLOCKCOUNT_BITLEN 21
+
+typedef struct xfs_bmbt_rec {
+       __be64                  l0, l1;
+} xfs_bmbt_rec_t;
+
+typedef __uint64_t     xfs_bmbt_rec_base_t;    /* use this for casts */
+typedef xfs_bmbt_rec_t xfs_bmdr_rec_t;
+
+typedef struct xfs_bmbt_rec_host {
+       __uint64_t              l0, l1;
+} xfs_bmbt_rec_host_t;
+
+/*
+ * Values and macros for delayed-allocation startblock fields.
+ */
+#define STARTBLOCKVALBITS      17
+#define STARTBLOCKMASKBITS     (15 + XFS_BIG_BLKNOS * 20)
+#define DSTARTBLOCKMASKBITS    (15 + 20)
+#define STARTBLOCKMASK         \
+       (((((xfs_fsblock_t)1) << STARTBLOCKMASKBITS) - 1) << STARTBLOCKVALBITS)
+#define DSTARTBLOCKMASK                \
+       (((((xfs_dfsbno_t)1) << DSTARTBLOCKMASKBITS) - 1) << STARTBLOCKVALBITS)
+
+static inline int isnullstartblock(xfs_fsblock_t x)
+{
+       return ((x) & STARTBLOCKMASK) == STARTBLOCKMASK;
+}
+
+static inline int isnulldstartblock(xfs_dfsbno_t x)
+{
+       return ((x) & DSTARTBLOCKMASK) == DSTARTBLOCKMASK;
+}
+
+static inline xfs_fsblock_t nullstartblock(int k)
+{
+       ASSERT(k < (1 << STARTBLOCKVALBITS));
+       return STARTBLOCKMASK | (k);
+}
+
+static inline xfs_filblks_t startblockval(xfs_fsblock_t x)
+{
+       return (xfs_filblks_t)((x) & ~STARTBLOCKMASK);
+}
+
+/*
+ * Possible extent formats.
+ */
+typedef enum {
+       XFS_EXTFMT_NOSTATE = 0,
+       XFS_EXTFMT_HASSTATE
+} xfs_exntfmt_t;
+
+/*
+ * Possible extent states.
+ */
+typedef enum {
+       XFS_EXT_NORM, XFS_EXT_UNWRITTEN,
+       XFS_EXT_DMAPI_OFFLINE, XFS_EXT_INVALID
+} xfs_exntst_t;
+
+/*
+ * Incore version of above.
+ */
+typedef struct xfs_bmbt_irec
+{
+       xfs_fileoff_t   br_startoff;    /* starting file offset */
+       xfs_fsblock_t   br_startblock;  /* starting block number */
+       xfs_filblks_t   br_blockcount;  /* number of blocks */
+       xfs_exntst_t    br_state;       /* extent state */
+} xfs_bmbt_irec_t;
+
+/*
+ * Key structure for non-leaf levels of the tree.
+ */
+typedef struct xfs_bmbt_key {
+       __be64          br_startoff;    /* starting file offset */
+} xfs_bmbt_key_t, xfs_bmdr_key_t;
+
+/* btree pointer type */
+typedef __be64 xfs_bmbt_ptr_t, xfs_bmdr_ptr_t;
+
+
+/*
+ * Generic Btree block format definitions
+ *
+ * This is a combination of the actual format used on disk for short and long
+ * format btrees.  The first three fields are shared by both format, but the
+ * pointers are different and should be used with care.
+ *
+ * To get the size of the actual short or long form headers please use the size
+ * macros below.  Never use sizeof(xfs_btree_block).
+ *
+ * The blkno, crc, lsn, owner and uuid fields are only available in filesystems
+ * with the crc feature bit, and all accesses to them must be conditional on
+ * that flag.
+ */
+struct xfs_btree_block {
+       __be32          bb_magic;       /* magic number for block type */
+       __be16          bb_level;       /* 0 is a leaf */
+       __be16          bb_numrecs;     /* current # of data records */
+       union {
+               struct {
+                       __be32          bb_leftsib;
+                       __be32          bb_rightsib;
+
+                       __be64          bb_blkno;
+                       __be64          bb_lsn;
+                       uuid_t          bb_uuid;
+                       __be32          bb_owner;
+                       __le32          bb_crc;
+               } s;                    /* short form pointers */
+               struct  {
+                       __be64          bb_leftsib;
+                       __be64          bb_rightsib;
+
+                       __be64          bb_blkno;
+                       __be64          bb_lsn;
+                       uuid_t          bb_uuid;
+                       __be64          bb_owner;
+                       __le32          bb_crc;
+                       __be32          bb_pad; /* padding for alignment */
+               } l;                    /* long form pointers */
+       } bb_u;                         /* rest */
+};
+
+#define XFS_BTREE_SBLOCK_LEN   16      /* size of a short form block */
+#define XFS_BTREE_LBLOCK_LEN   24      /* size of a long form block */
+
+/* sizes of CRC enabled btree blocks */
+#define XFS_BTREE_SBLOCK_CRC_LEN       (XFS_BTREE_SBLOCK_LEN + 40)
+#define XFS_BTREE_LBLOCK_CRC_LEN       (XFS_BTREE_LBLOCK_LEN + 48)
+
+#define XFS_BTREE_SBLOCK_CRC_OFF \
+       offsetof(struct xfs_btree_block, bb_u.s.bb_crc)
+#define XFS_BTREE_LBLOCK_CRC_OFF \
+       offsetof(struct xfs_btree_block, bb_u.l.bb_crc)
 
 #endif /* __XFS_FORMAT_H__ */
index 18272c766a508ab53deb5465448ca26bf02110d6..c5fc116dfaa307b156e0b652bfffd4f80c173b89 100644 (file)
@@ -233,11 +233,11 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_LOGV2      0x0100  /* log format version 2 */
 #define XFS_FSOP_GEOM_FLAGS_SECTOR     0x0200  /* sector sizes >1BB    */
 #define XFS_FSOP_GEOM_FLAGS_ATTR2      0x0400  /* inline attributes rework */
-#define XFS_FSOP_GEOM_FLAGS_PROJID32   0x0800  /* 32-bit project IDs   */
+#define XFS_FSOP_GEOM_FLAGS_PROJID32   0x0800  /* 32-bit project IDs   */
 #define XFS_FSOP_GEOM_FLAGS_DIRV2CI    0x1000  /* ASCII only CI names  */
 #define XFS_FSOP_GEOM_FLAGS_LAZYSB     0x4000  /* lazy superblock counters */
 #define XFS_FSOP_GEOM_FLAGS_V5SB       0x8000  /* version 5 superblock */
-
+#define XFS_FSOP_GEOM_FLAGS_FTYPE      0x10000 /* inode directory types */
 
 /*
  * Minimum and maximum sizes need for growth checks.
index e64ee5288b86be2d0c0267b383d3f6f9297e60a1..a6e54b3319bd0f5deb573623f332486590fbe165 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
-#include "xfs_btree.h"
 #include "xfs_error.h"
+#include "xfs_btree.h"
+#include "xfs_alloc_btree.h"
 #include "xfs_alloc.h"
 #include "xfs_ialloc.h"
 #include "xfs_fsops.h"
 #include "xfs_itable.h"
 #include "xfs_trans_space.h"
 #include "xfs_rtalloc.h"
-#include "xfs_filestream.h"
 #include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_dinode.h"
+#include "xfs_filestream.h"
 
 /*
  * File system operations
@@ -101,7 +102,9 @@ xfs_fs_geometry(
                        (xfs_sb_version_hasprojid32bit(&mp->m_sb) ?
                                XFS_FSOP_GEOM_FLAGS_PROJID32 : 0) |
                        (xfs_sb_version_hascrc(&mp->m_sb) ?
-                               XFS_FSOP_GEOM_FLAGS_V5SB : 0);
+                               XFS_FSOP_GEOM_FLAGS_V5SB : 0) |
+                       (xfs_sb_version_hasftype(&mp->m_sb) ?
+                               XFS_FSOP_GEOM_FLAGS_FTYPE : 0);
                geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ?
                                mp->m_sb.sb_logsectsize : BBSIZE;
                geo->rtsectsize = mp->m_sb.sb_blocksize;
@@ -153,7 +156,7 @@ xfs_growfs_data_private(
        xfs_buf_t               *bp;
        int                     bucket;
        int                     dpct;
-       int                     error;
+       int                     error, saved_error = 0;
        xfs_agnumber_t          nagcount;
        xfs_agnumber_t          nagimax = 0;
        xfs_rfsblock_t          nb, nb_mod;
@@ -496,29 +499,33 @@ xfs_growfs_data_private(
                                error = ENOMEM;
                }
 
+               /*
+                * If we get an error reading or writing alternate superblocks,
+                * continue.  xfs_repair chooses the "best" superblock based
+                * on most matches; if we break early, we'll leave more
+                * superblocks un-updated than updated, and xfs_repair may
+                * pick them over the properly-updated primary.
+                */
                if (error) {
                        xfs_warn(mp,
                "error %d reading secondary superblock for ag %d",
                                error, agno);
-                       break;
+                       saved_error = error;
+                       continue;
                }
                xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS);
 
-               /*
-                * If we get an error writing out the alternate superblocks,
-                * just issue a warning and continue.  The real work is
-                * already done and committed.
-                */
                error = xfs_bwrite(bp);
                xfs_buf_relse(bp);
                if (error) {
                        xfs_warn(mp,
                "write error %d updating secondary superblock for ag %d",
                                error, agno);
-                       break; /* no point in continuing */
+                       saved_error = error;
+                       continue;
                }
        }
-       return error;
+       return saved_error ? saved_error : error;
 
  error0:
        xfs_trans_cancel(tp, XFS_TRANS_ABORT);
index ccf2fb1439629fae273a239625f97f6879192878..e87719c5bebe128988a0993c4dc0f703cd3733ca 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
 #include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
 #include "xfs_alloc.h"
 #include "xfs_rtalloc.h"
 #include "xfs_error.h"
 #include "xfs_bmap.h"
 #include "xfs_cksum.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_icreate_item.h"
 #include "xfs_icache.h"
+#include "xfs_dinode.h"
+#include "xfs_trace.h"
 
 
 /*
@@ -1627,8 +1628,9 @@ xfs_read_agi(
 {
        int                     error;
 
-       ASSERT(agno != NULLAGNUMBER);
+       trace_xfs_read_agi(mp, agno);
 
+       ASSERT(agno != NULLAGNUMBER);
        error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
                        XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
                        XFS_FSS_TO_BB(mp, 1), 0, bpp, &xfs_agi_buf_ops);
@@ -1651,6 +1653,8 @@ xfs_ialloc_read_agi(
        struct xfs_perag        *pag;   /* per allocation group data */
        int                     error;
 
+       trace_xfs_ialloc_read_agi(mp, agno);
+
        error = xfs_read_agi(mp, tp, agno, bpp);
        if (error)
                return error;
index 68c07320f096e53d31f3050850414e1038c79c4a..a8f76a5ff4184b316c53b78cb22445006926e90f 100644 (file)
@@ -23,6 +23,7 @@ struct xfs_dinode;
 struct xfs_imap;
 struct xfs_mount;
 struct xfs_trans;
+struct xfs_btree_cur;
 
 /*
  * Allocation parameters for inode allocation.
@@ -42,7 +43,7 @@ struct xfs_trans;
 static inline struct xfs_dinode *
 xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o)
 {
-       return (xfs_dinode_t *)
+       return (struct xfs_dinode *)
                (xfs_buf_offset(b, o << (mp)->m_sb.sb_inodelog));
 }
 
@@ -158,6 +159,4 @@ int xfs_ialloc_inode_init(struct xfs_mount *mp, struct xfs_trans *tp,
                          xfs_agnumber_t agno, xfs_agblock_t agbno,
                          xfs_agblock_t length, unsigned int gen);
 
-extern const struct xfs_buf_ops xfs_agi_buf_ops;
-
 #endif /* __XFS_IALLOC_H__ */
index 5448eb6b8c12ad1acdf9d621750ec39e6c280a80..c8fa5bbb36de3163b9fc798fc80025815f46182e 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
 #include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
 #include "xfs_alloc.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
+#include "xfs_trans.h"
 
 
 STATIC int
index 3ac36b7642e9a09960ead79d38583b08e1be1213..f38b22011c4e4604a5e9e74ec9f4afe344dccfdb 100644 (file)
@@ -26,55 +26,6 @@ struct xfs_buf;
 struct xfs_btree_cur;
 struct xfs_mount;
 
-/*
- * There is a btree for the inode map per allocation group.
- */
-#define        XFS_IBT_MAGIC           0x49414254      /* 'IABT' */
-#define        XFS_IBT_CRC_MAGIC       0x49414233      /* 'IAB3' */
-
-typedef        __uint64_t      xfs_inofree_t;
-#define        XFS_INODES_PER_CHUNK            (NBBY * sizeof(xfs_inofree_t))
-#define        XFS_INODES_PER_CHUNK_LOG        (XFS_NBBYLOG + 3)
-#define        XFS_INOBT_ALL_FREE              ((xfs_inofree_t)-1)
-#define        XFS_INOBT_MASK(i)               ((xfs_inofree_t)1 << (i))
-
-static inline xfs_inofree_t xfs_inobt_maskn(int i, int n)
-{
-       return ((n >= XFS_INODES_PER_CHUNK ? 0 : XFS_INOBT_MASK(n)) - 1) << i;
-}
-
-/*
- * Data record structure
- */
-typedef struct xfs_inobt_rec {
-       __be32          ir_startino;    /* starting inode number */
-       __be32          ir_freecount;   /* count of free inodes (set bits) */
-       __be64          ir_free;        /* free inode mask */
-} xfs_inobt_rec_t;
-
-typedef struct xfs_inobt_rec_incore {
-       xfs_agino_t     ir_startino;    /* starting inode number */
-       __int32_t       ir_freecount;   /* count of free inodes (set bits) */
-       xfs_inofree_t   ir_free;        /* free inode mask */
-} xfs_inobt_rec_incore_t;
-
-
-/*
- * Key structure
- */
-typedef struct xfs_inobt_key {
-       __be32          ir_startino;    /* starting inode number */
-} xfs_inobt_key_t;
-
-/* btree pointer type */
-typedef __be32 xfs_inobt_ptr_t;
-
-/*
- * block numbers in the AG.
- */
-#define        XFS_IBT_BLOCK(mp)               ((xfs_agblock_t)(XFS_CNT_BLOCK(mp) + 1))
-#define        XFS_PREALLOC_BLOCKS(mp)         ((xfs_agblock_t)(XFS_IBT_BLOCK(mp) + 1))
-
 /*
  * Btree block header size depends on a superblock flag.
  */
@@ -110,6 +61,4 @@ extern struct xfs_btree_cur *xfs_inobt_init_cursor(struct xfs_mount *,
                struct xfs_trans *, struct xfs_buf *, xfs_agnumber_t);
 extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
 
-extern const struct xfs_buf_ops xfs_inobt_buf_ops;
-
 #endif /* __XFS_IALLOC_BTREE_H__ */
index 474807a401c864e7681d3f8d0f111358c45536fc..98d35244eecc936bdb0a9702ae70a9de1d980de8 100644 (file)
 #include "xfs.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_log_priv.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
-#include "xfs_dinode.h"
 #include "xfs_error.h"
-#include "xfs_filestream.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
 #include "xfs_inode_item.h"
 #include "xfs_quota.h"
 #include "xfs_trace.h"
-#include "xfs_fsops.h"
 #include "xfs_icache.h"
 #include "xfs_bmap_util.h"
 
@@ -500,11 +495,6 @@ xfs_inode_ag_walk_grab(
        if (!igrab(inode))
                return ENOENT;
 
-       if (is_bad_inode(inode)) {
-               IRELE(ip);
-               return ENOENT;
-       }
-
        /* inode is valid */
        return 0;
 
@@ -918,8 +908,6 @@ restart:
                xfs_iflock(ip);
        }
 
-       if (is_bad_inode(VFS_I(ip)))
-               goto reclaim;
        if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
                xfs_iunpin_wait(ip);
                xfs_iflush_abort(ip, false);
index 5a5a593994d4196d3b18c2e959df90dc28c7588f..d2eaccfa73f4b14c622e66b39787396433e87604 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
 #include "xfs_error.h"
 #include "xfs_icreate_item.h"
index e3d75385aa76a6e45b7711a65bb39c268c9f689b..001aa893ed594020d76bc30cdffb05dfeaa24371 100644 (file)
 
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_trans_space.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_da_format.h"
 #include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
 #include "xfs_attr_sf.h"
 #include "xfs_attr.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
+#include "xfs_trans_space.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_inode_item.h"
-#include "xfs_btree.h"
-#include "xfs_alloc.h"
 #include "xfs_ialloc.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_error.h"
 #include "xfs_quota.h"
+#include "xfs_dinode.h"
 #include "xfs_filestream.h"
 #include "xfs_cksum.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
 #include "xfs_symlink.h"
+#include "xfs_trans_priv.h"
+#include "xfs_log.h"
+#include "xfs_bmap_btree.h"
 
 kmem_zone_t *xfs_inode_zone;
 
@@ -1662,6 +1661,126 @@ xfs_release(
        return 0;
 }
 
+/*
+ * xfs_inactive_truncate
+ *
+ * Called to perform a truncate when an inode becomes unlinked.
+ */
+STATIC int
+xfs_inactive_truncate(
+       struct xfs_inode *ip)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0);
+       if (error) {
+               ASSERT(XFS_FORCED_SHUTDOWN(mp));
+               xfs_trans_cancel(tp, 0);
+               return error;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /*
+        * Log the inode size first to prevent stale data exposure in the event
+        * of a system crash before the truncate completes. See the related
+        * comment in xfs_setattr_size() for details.
+        */
+       ip->i_d.di_size = 0;
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+       error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0);
+       if (error)
+               goto error_trans_cancel;
+
+       ASSERT(ip->i_d.di_nextents == 0);
+
+       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+       if (error)
+               goto error_unlock;
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return 0;
+
+error_trans_cancel:
+       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+error_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+}
+
+/*
+ * xfs_inactive_ifree()
+ *
+ * Perform the inode free when an inode is unlinked.
+ */
+STATIC int
+xfs_inactive_ifree(
+       struct xfs_inode *ip)
+{
+       xfs_bmap_free_t         free_list;
+       xfs_fsblock_t           first_block;
+       int                     committed;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ifree, 0, 0);
+       if (error) {
+               ASSERT(XFS_FORCED_SHUTDOWN(mp));
+               xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES);
+               return error;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       xfs_bmap_init(&free_list, &first_block);
+       error = xfs_ifree(tp, ip, &free_list);
+       if (error) {
+               /*
+                * If we fail to free the inode, shut down.  The cancel
+                * might do that, we need to make sure.  Otherwise the
+                * inode might be lost for a long time or forever.
+                */
+               if (!XFS_FORCED_SHUTDOWN(mp)) {
+                       xfs_notice(mp, "%s: xfs_ifree returned error %d",
+                               __func__, error);
+                       xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
+               }
+               xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               return error;
+       }
+
+       /*
+        * Credit the quota account(s). The inode is gone.
+        */
+       xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_ICOUNT, -1);
+
+       /*
+        * Just ignore errors at this point.  There is nothing we can
+        * do except to try to keep going. Make sure it's not a silent
+        * error.
+        */
+       error = xfs_bmap_finish(&tp,  &free_list, &committed);
+       if (error)
+               xfs_notice(mp, "%s: xfs_bmap_finish returned error %d",
+                       __func__, error);
+       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+       if (error)
+               xfs_notice(mp, "%s: xfs_trans_commit returned error %d",
+                       __func__, error);
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return 0;
+}
+
 /*
  * xfs_inactive
  *
@@ -1670,16 +1789,11 @@ xfs_release(
  * now be truncated.  Also, we clear all of the read-ahead state
  * kept for the inode here since the file is now closed.
  */
-int
+void
 xfs_inactive(
        xfs_inode_t     *ip)
 {
-       xfs_bmap_free_t         free_list;
-       xfs_fsblock_t           first_block;
-       int                     committed;
-       struct xfs_trans        *tp;
        struct xfs_mount        *mp;
-       struct xfs_trans_res    *resp;
        int                     error;
        int                     truncate = 0;
 
@@ -1687,19 +1801,17 @@ xfs_inactive(
         * If the inode is already free, then there can be nothing
         * to clean up here.
         */
-       if (ip->i_d.di_mode == 0 || is_bad_inode(VFS_I(ip))) {
+       if (ip->i_d.di_mode == 0) {
                ASSERT(ip->i_df.if_real_bytes == 0);
                ASSERT(ip->i_df.if_broot_bytes == 0);
-               return VN_INACTIVE_CACHE;
+               return;
        }
 
        mp = ip->i_mount;
 
-       error = 0;
-
        /* If this is a read-only mount, don't do this (would generate I/O) */
        if (mp->m_flags & XFS_MOUNT_RDONLY)
-               goto out;
+               return;
 
        if (ip->i_d.di_nlink != 0) {
                /*
@@ -1707,12 +1819,10 @@ xfs_inactive(
                 * cache. Post-eof blocks must be freed, lest we end up with
                 * broken free space accounting.
                 */
-               if (xfs_can_free_eofblocks(ip, true)) {
-                       error = xfs_free_eofblocks(mp, ip, false);
-                       if (error)
-                               return VN_INACTIVE_CACHE;
-               }
-               goto out;
+               if (xfs_can_free_eofblocks(ip, true))
+                       xfs_free_eofblocks(mp, ip, false);
+
+               return;
        }
 
        if (S_ISREG(ip->i_d.di_mode) &&
@@ -1722,36 +1832,14 @@ xfs_inactive(
 
        error = xfs_qm_dqattach(ip, 0);
        if (error)
-               return VN_INACTIVE_CACHE;
+               return;
 
-       tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
-       resp = (truncate || S_ISLNK(ip->i_d.di_mode)) ?
-               &M_RES(mp)->tr_itruncate : &M_RES(mp)->tr_ifree;
-
-       error = xfs_trans_reserve(tp, resp, 0, 0);
-       if (error) {
-               ASSERT(XFS_FORCED_SHUTDOWN(mp));
-               xfs_trans_cancel(tp, 0);
-               return VN_INACTIVE_CACHE;
-       }
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       xfs_trans_ijoin(tp, ip, 0);
-
-       if (S_ISLNK(ip->i_d.di_mode)) {
-               error = xfs_inactive_symlink(ip, &tp);
-               if (error)
-                       goto out_cancel;
-       } else if (truncate) {
-               ip->i_d.di_size = 0;
-               xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-
-               error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0);
-               if (error)
-                       goto out_cancel;
-
-               ASSERT(ip->i_d.di_nextents == 0);
-       }
+       if (S_ISLNK(ip->i_d.di_mode))
+               error = xfs_inactive_symlink(ip);
+       else if (truncate)
+               error = xfs_inactive_truncate(ip);
+       if (error)
+               return;
 
        /*
         * If there are attributes associated with the file then blow them away
@@ -1762,25 +1850,9 @@ xfs_inactive(
        if (ip->i_d.di_anextents > 0) {
                ASSERT(ip->i_d.di_forkoff != 0);
 
-               error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
-               if (error)
-                       goto out_unlock;
-
-               xfs_iunlock(ip, XFS_ILOCK_EXCL);
-
                error = xfs_attr_inactive(ip);
                if (error)
-                       goto out;
-
-               tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
-               error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ifree, 0, 0);
-               if (error) {
-                       xfs_trans_cancel(tp, 0);
-                       goto out;
-               }
-
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               xfs_trans_ijoin(tp, ip, 0);
+                       return;
        }
 
        if (ip->i_afp)
@@ -1791,52 +1863,14 @@ xfs_inactive(
        /*
         * Free the inode.
         */
-       xfs_bmap_init(&free_list, &first_block);
-       error = xfs_ifree(tp, ip, &free_list);
-       if (error) {
-               /*
-                * If we fail to free the inode, shut down.  The cancel
-                * might do that, we need to make sure.  Otherwise the
-                * inode might be lost for a long time or forever.
-                */
-               if (!XFS_FORCED_SHUTDOWN(mp)) {
-                       xfs_notice(mp, "%s: xfs_ifree returned error %d",
-                               __func__, error);
-                       xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
-               }
-               xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
-       } else {
-               /*
-                * Credit the quota account(s). The inode is gone.
-                */
-               xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_ICOUNT, -1);
-
-               /*
-                * Just ignore errors at this point.  There is nothing we can
-                * do except to try to keep going. Make sure it's not a silent
-                * error.
-                */
-               error = xfs_bmap_finish(&tp,  &free_list, &committed);
-               if (error)
-                       xfs_notice(mp, "%s: xfs_bmap_finish returned error %d",
-                               __func__, error);
-               error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
-               if (error)
-                       xfs_notice(mp, "%s: xfs_trans_commit returned error %d",
-                               __func__, error);
-       }
+       error = xfs_inactive_ifree(ip);
+       if (error)
+               return;
 
        /*
         * Release the dquots held by inode, if any.
         */
        xfs_qm_dqdetach(ip);
-out_unlock:
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-out:
-       return VN_INACTIVE_CACHE;
-out_cancel:
-       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
-       goto out_unlock;
 }
 
 /*
@@ -2370,6 +2404,33 @@ xfs_iunpin_wait(
                __xfs_iunpin_wait(ip);
 }
 
+/*
+ * Removing an inode from the namespace involves removing the directory entry
+ * and dropping the link count on the inode. Removing the directory entry can
+ * result in locking an AGF (directory blocks were freed) and removing a link
+ * count can result in placing the inode on an unlinked list which results in
+ * locking an AGI.
+ *
+ * The big problem here is that we have an ordering constraint on AGF and AGI
+ * locking - inode allocation locks the AGI, then can allocate a new extent for
+ * new inodes, locking the AGF after the AGI. Similarly, freeing the inode
+ * removes the inode from the unlinked list, requiring that we lock the AGI
+ * first, and then freeing the inode can result in an inode chunk being freed
+ * and hence freeing disk space requiring that we lock an AGF.
+ *
+ * Hence the ordering that is imposed by other parts of the code is AGI before
+ * AGF. This means we cannot remove the directory entry before we drop the inode
+ * reference count and put it on the unlinked list as this results in a lock
+ * order of AGF then AGI, and this can deadlock against inode allocation and
+ * freeing. Therefore we must drop the link counts before we remove the
+ * directory entry.
+ *
+ * This is still safe from a transactional point of view - it is not until we
+ * get to xfs_bmap_finish() that we have the possibility of multiple
+ * transactions in this operation. Hence as long as we remove the directory
+ * entry and drop the link count in the first transaction of the remove
+ * operation, there are no transactional constraints on the ordering here.
+ */
 int
 xfs_remove(
        xfs_inode_t             *dp,
@@ -2439,6 +2500,7 @@ xfs_remove(
        /*
         * If we're removing a directory perform some additional validation.
         */
+       cancel_flags |= XFS_TRANS_ABORT;
        if (is_dir) {
                ASSERT(ip->i_d.di_nlink >= 2);
                if (ip->i_d.di_nlink != 2) {
@@ -2449,31 +2511,16 @@ xfs_remove(
                        error = XFS_ERROR(ENOTEMPTY);
                        goto out_trans_cancel;
                }
-       }
 
-       xfs_bmap_init(&free_list, &first_block);
-       error = xfs_dir_removename(tp, dp, name, ip->i_ino,
-                                       &first_block, &free_list, resblks);
-       if (error) {
-               ASSERT(error != ENOENT);
-               goto out_bmap_cancel;
-       }
-       xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
-
-       if (is_dir) {
-               /*
-                * Drop the link from ip's "..".
-                */
+               /* Drop the link from ip's "..".  */
                error = xfs_droplink(tp, dp);
                if (error)
-                       goto out_bmap_cancel;
+                       goto out_trans_cancel;
 
-               /*
-                * Drop the "." link from ip to self.
-                */
+               /* Drop the "." link from ip to self.  */
                error = xfs_droplink(tp, ip);
                if (error)
-                       goto out_bmap_cancel;
+                       goto out_trans_cancel;
        } else {
                /*
                 * When removing a non-directory we need to log the parent
@@ -2482,20 +2529,24 @@ xfs_remove(
                 */
                xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
        }
+       xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
 
-       /*
-        * Drop the link from dp to ip.
-        */
+       /* Drop the link from dp to ip. */
        error = xfs_droplink(tp, ip);
        if (error)
-               goto out_bmap_cancel;
+               goto out_trans_cancel;
 
-       /*
-        * Determine if this is the last link while
-        * we are in the transaction.
-        */
+       /* Determine if this is the last link while the inode is locked */
        link_zero = (ip->i_d.di_nlink == 0);
 
+       xfs_bmap_init(&free_list, &first_block);
+       error = xfs_dir_removename(tp, dp, name, ip->i_ino,
+                                       &first_block, &free_list, resblks);
+       if (error) {
+               ASSERT(error != ENOENT);
+               goto out_bmap_cancel;
+       }
+
        /*
         * If this is a synchronous mount, make sure that the
         * remove transaction goes to disk before returning to
@@ -2525,7 +2576,6 @@ xfs_remove(
 
  out_bmap_cancel:
        xfs_bmap_cancel(&free_list);
-       cancel_flags |= XFS_TRANS_ABORT;
  out_trans_cancel:
        xfs_trans_cancel(tp, cancel_flags);
  std_return:
index 4a91358c1470b9ac029d451dd38c3fa77daf6fc0..9e6efccbae04f23f86581767e09f61304dc55ac4 100644 (file)
@@ -24,7 +24,6 @@
 /*
  * Kernel only inode definitions
  */
-
 struct xfs_dinode;
 struct xfs_inode;
 struct xfs_buf;
@@ -50,6 +49,9 @@ typedef struct xfs_inode {
        xfs_ifork_t             *i_afp;         /* attribute fork pointer */
        xfs_ifork_t             i_df;           /* data fork */
 
+       /* operations vectors */
+       const struct xfs_dir_ops *d_ops;                /* directory ops vector */
+
        /* Transaction and locking information. */
        struct xfs_inode_log_item *i_itemp;     /* logging information */
        mrlock_t                i_lock;         /* inode lock */
@@ -316,7 +318,7 @@ static inline int xfs_isiflocked(struct xfs_inode *ip)
 
 
 int            xfs_release(struct xfs_inode *ip);
-int            xfs_inactive(struct xfs_inode *ip);
+void           xfs_inactive(struct xfs_inode *ip);
 int            xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
                           struct xfs_inode **ipp, struct xfs_name *ci_name);
 int            xfs_create(struct xfs_inode *dp, struct xfs_name *name,
index 63382d37f5658c8ee774668df7a50a87eeec5834..4fc9f39dd89e7b8ed64e271ca6ced6bc43a191f6 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_error.h"
 #include "xfs_cksum.h"
 #include "xfs_icache.h"
+#include "xfs_trans.h"
 #include "xfs_ialloc.h"
+#include "xfs_dinode.h"
 
 /*
  * Check that none of the inode's in the buffer have a next
index abba0ae8cf2da2b4012445bc2cc5cbf63b8c9a66..9308c47f2a527dc08b75b66de5d064e0b13e0cfe 100644 (file)
@@ -47,7 +47,4 @@ void  xfs_inobp_check(struct xfs_mount *, struct xfs_buf *);
 #define        xfs_inobp_check(mp, bp)
 #endif /* DEBUG */
 
-extern const struct xfs_buf_ops xfs_inode_buf_ops;
-extern const struct xfs_buf_ops xfs_inode_buf_ra_ops;
-
 #endif /* __XFS_INODE_BUF_H__ */
index 02f1083955bb1dfa19c295adea18b663b25f93f7..cfee14a83cfe601d90bdc6732cc608746aaf3e03 100644 (file)
 #include "xfs.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_attr_sf.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_buf_item.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
-#include "xfs_btree.h"
-#include "xfs_alloc.h"
-#include "xfs_ialloc.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_bmap.h"
 #include "xfs_error.h"
-#include "xfs_quota.h"
-#include "xfs_filestream.h"
-#include "xfs_cksum.h"
 #include "xfs_trace.h"
-#include "xfs_icache.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
 
 kmem_zone_t *xfs_ifork_zone;
 
@@ -1031,15 +1021,14 @@ xfs_iext_add(
                 * the next index needed in the indirection array.
                 */
                else {
-                       int     count = ext_diff;
+                       uint    count = ext_diff;
 
                        while (count) {
                                erp = xfs_iext_irec_new(ifp, erp_idx);
-                               erp->er_extcount = count;
-                               count -= MIN(count, (int)XFS_LINEAR_EXTS);
-                               if (count) {
+                               erp->er_extcount = min(count, XFS_LINEAR_EXTS);
+                               count -= erp->er_extcount;
+                               if (count)
                                        erp_idx++;
-                               }
                        }
                }
        }
@@ -1359,7 +1348,7 @@ xfs_iext_remove_indirect(
 void
 xfs_iext_realloc_direct(
        xfs_ifork_t     *ifp,           /* inode fork pointer */
-       int             new_size)       /* new size of extents */
+       int             new_size)       /* new size of extents after adding */
 {
        int             rnew_size;      /* real new size of extents */
 
@@ -1397,13 +1386,8 @@ xfs_iext_realloc_direct(
                                rnew_size - ifp->if_real_bytes);
                }
        }
-       /*
-        * Switch from the inline extent buffer to a direct
-        * extent list. Be sure to include the inline extent
-        * bytes in new_size.
-        */
+       /* Switch from the inline extent buffer to a direct extent list */
        else {
-               new_size += ifp->if_bytes;
                if (!is_power_of_2(new_size)) {
                        rnew_size = roundup_pow_of_two(new_size);
                }
index 28661a0d90583bc2d20ab37089b79f921eaf135c..eb329a1ea8886a3d878489765bc5d13dbbf26068 100644 (file)
@@ -19,6 +19,7 @@
 #define        __XFS_INODE_FORK_H__
 
 struct xfs_inode_log_item;
+struct xfs_dinode;
 
 /*
  * The following xfs_ext_irec_t struct introduces a second (top) level
index 378081109844b09b2bbfd07dcb4214027fefe2c2..7c0d391f9a6e0bd3f4f6a72cc6f0e65323cc7183 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_trans_priv.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_inode_item.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_trans_priv.h"
+#include "xfs_dinode.h"
 
 
 kmem_zone_t    *xfs_ili_zone;          /* inode log item zone */
index 668e8f4ccf5e7201e8e6a360cd26668484f6d515..4d613401a5e056a08dd2f8b21b77833bfe14cbc6 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_ioctl.h"
+#include "xfs_alloc.h"
 #include "xfs_rtalloc.h"
 #include "xfs_itable.h"
 #include "xfs_error.h"
 #include "xfs_attr.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
-#include "xfs_buf_item.h"
 #include "xfs_fsops.h"
 #include "xfs_discard.h"
 #include "xfs_quota.h"
-#include "xfs_inode_item.h"
 #include "xfs_export.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
 #include "xfs_symlink.h"
+#include "xfs_dinode.h"
+#include "xfs_trans.h"
 
 #include <linux/capability.h>
 #include <linux/dcache.h>
@@ -641,7 +640,11 @@ xfs_ioc_space(
        unsigned int            cmd,
        xfs_flock64_t           *bf)
 {
-       int                     attr_flags = 0;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       struct iattr            iattr;
+       bool                    setprealloc = false;
+       bool                    clrprealloc = false;
        int                     error;
 
        /*
@@ -661,19 +664,128 @@ xfs_ioc_space(
        if (!S_ISREG(inode->i_mode))
                return -XFS_ERROR(EINVAL);
 
-       if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
-               attr_flags |= XFS_ATTR_NONBLOCK;
+       error = mnt_want_write_file(filp);
+       if (error)
+               return error;
 
-       if (filp->f_flags & O_DSYNC)
-               attr_flags |= XFS_ATTR_SYNC;
+       xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+       switch (bf->l_whence) {
+       case 0: /*SEEK_SET*/
+               break;
+       case 1: /*SEEK_CUR*/
+               bf->l_start += filp->f_pos;
+               break;
+       case 2: /*SEEK_END*/
+               bf->l_start += XFS_ISIZE(ip);
+               break;
+       default:
+               error = XFS_ERROR(EINVAL);
+               goto out_unlock;
+       }
 
-       if (ioflags & IO_INVIS)
-               attr_flags |= XFS_ATTR_DMI;
+       /*
+        * length of <= 0 for resv/unresv/zero is invalid.  length for
+        * alloc/free is ignored completely and we have no idea what userspace
+        * might have set it to, so set it to zero to allow range
+        * checks to pass.
+        */
+       switch (cmd) {
+       case XFS_IOC_ZERO_RANGE:
+       case XFS_IOC_RESVSP:
+       case XFS_IOC_RESVSP64:
+       case XFS_IOC_UNRESVSP:
+       case XFS_IOC_UNRESVSP64:
+               if (bf->l_len <= 0) {
+                       error = XFS_ERROR(EINVAL);
+                       goto out_unlock;
+               }
+               break;
+       default:
+               bf->l_len = 0;
+               break;
+       }
+
+       if (bf->l_start < 0 ||
+           bf->l_start > mp->m_super->s_maxbytes ||
+           bf->l_start + bf->l_len < 0 ||
+           bf->l_start + bf->l_len >= mp->m_super->s_maxbytes) {
+               error = XFS_ERROR(EINVAL);
+               goto out_unlock;
+       }
+
+       switch (cmd) {
+       case XFS_IOC_ZERO_RANGE:
+               error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
+               if (!error)
+                       setprealloc = true;
+               break;
+       case XFS_IOC_RESVSP:
+       case XFS_IOC_RESVSP64:
+               error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len,
+                                               XFS_BMAPI_PREALLOC);
+               if (!error)
+                       setprealloc = true;
+               break;
+       case XFS_IOC_UNRESVSP:
+       case XFS_IOC_UNRESVSP64:
+               error = xfs_free_file_space(ip, bf->l_start, bf->l_len);
+               break;
+       case XFS_IOC_ALLOCSP:
+       case XFS_IOC_ALLOCSP64:
+       case XFS_IOC_FREESP:
+       case XFS_IOC_FREESP64:
+               if (bf->l_start > XFS_ISIZE(ip)) {
+                       error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
+                                       bf->l_start - XFS_ISIZE(ip), 0);
+                       if (error)
+                               goto out_unlock;
+               }
+
+               iattr.ia_valid = ATTR_SIZE;
+               iattr.ia_size = bf->l_start;
+
+               error = xfs_setattr_size(ip, &iattr);
+               if (!error)
+                       clrprealloc = true;
+               break;
+       default:
+               ASSERT(0);
+               error = XFS_ERROR(EINVAL);
+       }
 
-       error = mnt_want_write_file(filp);
        if (error)
-               return error;
-       error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags);
+               goto out_unlock;
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               goto out_unlock;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+       if (!(ioflags & IO_INVIS)) {
+               ip->i_d.di_mode &= ~S_ISUID;
+               if (ip->i_d.di_mode & S_IXGRP)
+                       ip->i_d.di_mode &= ~S_ISGID;
+               xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+       }
+
+       if (setprealloc)
+               ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
+       else if (clrprealloc)
+               ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
+
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+       if (filp->f_flags & O_DSYNC)
+               xfs_trans_set_sync(tp);
+       error = xfs_trans_commit(tp, 0);
+
+out_unlock:
+       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        mnt_drop_write_file(filp);
        return -error;
 }
index f671f7e472ac008511ca4df2068c9d4fb91d169c..e8fb1231db8124dc08b2ffbcc551d6bfa1bc21f5 100644 (file)
 #include <asm/uaccess.h>
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_vnode.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_itable.h"
 #include "xfs_error.h"
index 8d4d49b6fbf347b3add01ed4675b489a653a6dfb..22d1cbea283d4734515218ef65b23ec78bfdeff6 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_inode_item.h"
 #include "xfs_btree.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
-#include "xfs_rtalloc.h"
 #include "xfs_error.h"
-#include "xfs_itable.h"
-#include "xfs_attr.h"
-#include "xfs_buf_item.h"
+#include "xfs_trans.h"
 #include "xfs_trans_space.h"
 #include "xfs_iomap.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
+#include "xfs_quota.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_dinode.h"
 
 
 #define XFS_WRITEIO_ALIGN(mp,off)      (((off) >> mp->m_writeio_log) \
@@ -110,7 +104,7 @@ xfs_alert_fsblock_zero(
        xfs_alert_tag(ip->i_mount, XFS_PTAG_FSBLOCK_ZERO,
                        "Access to block zero in inode %llu "
                        "start_block: %llx start_off: %llx "
-                       "blkcnt: %llx extent-state: %x\n",
+                       "blkcnt: %llx extent-state: %x",
                (unsigned long long)ip->i_ino,
                (unsigned long long)imap->br_startblock,
                (unsigned long long)imap->br_startoff,
@@ -655,7 +649,6 @@ int
 xfs_iomap_write_allocate(
        xfs_inode_t     *ip,
        xfs_off_t       offset,
-       size_t          count,
        xfs_bmbt_irec_t *imap)
 {
        xfs_mount_t     *mp = ip->i_mount;
index 80615760959ae169dddb471af1964c4ee03f448e..411fbb8919ef02456e82bb39c7b97443bf1e4da0 100644 (file)
 struct xfs_inode;
 struct xfs_bmbt_irec;
 
-extern int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t,
+int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t,
                        struct xfs_bmbt_irec *, int);
-extern int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t,
+int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t,
                        struct xfs_bmbt_irec *);
-extern int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t, size_t,
+int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t,
                        struct xfs_bmbt_irec *);
-extern int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, size_t);
+int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, size_t);
 
 #endif /* __XFS_IOMAP_H__*/
index 2b8952d9199bbd145473a48b326120b8d43ed9b6..27e0e544e9635ba47281279c68670f7e568a7a58 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_acl.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
-#include "xfs_rtalloc.h"
+#include "xfs_acl.h"
+#include "xfs_quota.h"
 #include "xfs_error.h"
-#include "xfs_itable.h"
 #include "xfs_attr.h"
-#include "xfs_buf_item.h"
-#include "xfs_inode_item.h"
+#include "xfs_trans.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
 #include "xfs_symlink.h"
 #include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2_priv.h"
+#include "xfs_dinode.h"
 
 #include <linux/capability.h>
 #include <linux/xattr.h>
@@ -709,8 +705,7 @@ out_dqrele:
 int
 xfs_setattr_size(
        struct xfs_inode        *ip,
-       struct iattr            *iattr,
-       int                     flags)
+       struct iattr            *iattr)
 {
        struct xfs_mount        *mp = ip->i_mount;
        struct inode            *inode = VFS_I(ip);
@@ -733,15 +728,11 @@ xfs_setattr_size(
        if (error)
                return XFS_ERROR(error);
 
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        ASSERT(S_ISREG(ip->i_d.di_mode));
        ASSERT((mask & (ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_ATIME_SET|
                        ATTR_MTIME_SET|ATTR_KILL_PRIV|ATTR_TIMES_SET)) == 0);
 
-       if (!(flags & XFS_ATTR_NOLOCK)) {
-               lock_flags |= XFS_IOLOCK_EXCL;
-               xfs_ilock(ip, lock_flags);
-       }
-
        oldsize = inode->i_size;
        newsize = iattr->ia_size;
 
@@ -750,12 +741,11 @@ xfs_setattr_size(
         */
        if (newsize == 0 && oldsize == 0 && ip->i_d.di_nextents == 0) {
                if (!(mask & (ATTR_CTIME|ATTR_MTIME)))
-                       goto out_unlock;
+                       return 0;
 
                /*
                 * Use the regular setattr path to update the timestamps.
                 */
-               xfs_iunlock(ip, lock_flags);
                iattr->ia_valid &= ~ATTR_SIZE;
                return xfs_setattr_nonsize(ip, iattr, 0);
        }
@@ -765,7 +755,7 @@ xfs_setattr_size(
         */
        error = xfs_qm_dqattach(ip, 0);
        if (error)
-               goto out_unlock;
+               return error;
 
        /*
         * Now we can make the changes.  Before we join the inode to the
@@ -783,7 +773,7 @@ xfs_setattr_size(
                 */
                error = xfs_zero_eof(ip, newsize, oldsize);
                if (error)
-                       goto out_unlock;
+                       return error;
        }
 
        /*
@@ -802,7 +792,7 @@ xfs_setattr_size(
                error = -filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
                                                      ip->i_d.di_size, newsize);
                if (error)
-                       goto out_unlock;
+                       return error;
        }
 
        /*
@@ -812,7 +802,7 @@ xfs_setattr_size(
 
        error = -block_truncate_page(inode->i_mapping, newsize, xfs_get_blocks);
        if (error)
-               goto out_unlock;
+               return error;
 
        tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE);
        error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0);
@@ -916,12 +906,21 @@ out_trans_cancel:
 
 STATIC int
 xfs_vn_setattr(
-       struct dentry   *dentry,
-       struct iattr    *iattr)
+       struct dentry           *dentry,
+       struct iattr            *iattr)
 {
-       if (iattr->ia_valid & ATTR_SIZE)
-               return -xfs_setattr_size(XFS_I(dentry->d_inode), iattr, 0);
-       return -xfs_setattr_nonsize(XFS_I(dentry->d_inode), iattr, 0);
+       struct xfs_inode        *ip = XFS_I(dentry->d_inode);
+       int                     error;
+
+       if (iattr->ia_valid & ATTR_SIZE) {
+               xfs_ilock(ip, XFS_IOLOCK_EXCL);
+               error = xfs_setattr_size(ip, iattr);
+               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+       } else {
+               error = xfs_setattr_nonsize(ip, iattr, 0);
+       }
+
+       return -error;
 }
 
 STATIC int
@@ -1169,6 +1168,7 @@ xfs_setup_inode(
        struct xfs_inode        *ip)
 {
        struct inode            *inode = &ip->i_vnode;
+       gfp_t                   gfp_mask;
 
        inode->i_ino = ip->i_ino;
        inode->i_state = I_NEW;
@@ -1204,6 +1204,7 @@ xfs_setup_inode(
        inode->i_ctime.tv_nsec  = ip->i_d.di_ctime.t_nsec;
        xfs_diflags_to_iflags(inode, ip);
 
+       ip->d_ops = ip->i_mount->m_nondir_inode_ops;
        switch (inode->i_mode & S_IFMT) {
        case S_IFREG:
                inode->i_op = &xfs_inode_operations;
@@ -1216,6 +1217,7 @@ xfs_setup_inode(
                else
                        inode->i_op = &xfs_dir_inode_operations;
                inode->i_fop = &xfs_dir_file_operations;
+               ip->d_ops = ip->i_mount->m_dir_inode_ops;
                break;
        case S_IFLNK:
                inode->i_op = &xfs_symlink_inode_operations;
@@ -1228,6 +1230,14 @@ xfs_setup_inode(
                break;
        }
 
+       /*
+        * Ensure all page cache allocations are done from GFP_NOFS context to
+        * prevent direct reclaim recursion back into the filesystem and blowing
+        * stacks or deadlocking.
+        */
+       gfp_mask = mapping_gfp_mask(inode->i_mapping);
+       mapping_set_gfp_mask(inode->i_mapping, (gfp_mask & ~(__GFP_FS)));
+
        /*
         * If there is no attribute fork no ACL can exist on this inode,
         * and it can't have any file capabilities attached to it either.
index d81fb41205ec97b9a00ecc0789e99763adc5af71..d2c5057b5cc4b4b701d05478e045cbf02a3ed408 100644 (file)
@@ -30,14 +30,10 @@ extern void xfs_setup_inode(struct xfs_inode *);
 /*
  * Internal setattr interfaces.
  */
-#define        XFS_ATTR_DMI            0x01    /* invocation from a DMI function */
-#define        XFS_ATTR_NONBLOCK       0x02    /* return EAGAIN if op would block */
-#define XFS_ATTR_NOLOCK                0x04    /* Don't grab any conflicting locks */
-#define XFS_ATTR_NOACL         0x08    /* Don't call xfs_acl_chmod */
-#define XFS_ATTR_SYNC          0x10    /* synchronous operation required */
+#define XFS_ATTR_NOACL         0x01    /* Don't call xfs_acl_chmod */
 
 extern int xfs_setattr_nonsize(struct xfs_inode *ip, struct iattr *vap,
                               int flags);
-extern int xfs_setattr_size(struct xfs_inode *ip, struct iattr *vap, int flags);
+extern int xfs_setattr_size(struct xfs_inode *ip, struct iattr *vap);
 
 #endif /* __XFS_IOPS_H__ */
index 084b3e1741fd0346cac1d8279ed8085553ee7486..c237ad15d500f767b81014a428720a7351431077 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_btree.h"
 #include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
 #include "xfs_itable.h"
 #include "xfs_error.h"
-#include "xfs_btree.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
+#include "xfs_dinode.h"
 
 STATIC int
 xfs_internal_inum(
index a2dea108071ae6e81d0e683a98a4a011b74f23ee..8497a00e399d0ba5960117c977376ae4a23cbbb3 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
 #include "xfs_error.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_log.h"
 #include "xfs_log_priv.h"
-#include "xfs_buf_item.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
 #include "xfs_log_recover.h"
-#include "xfs_trans_priv.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_trace.h"
 #include "xfs_fsops.h"
@@ -1000,27 +998,34 @@ xfs_log_space_wake(
 }
 
 /*
- * Determine if we have a transaction that has gone to disk
- * that needs to be covered. To begin the transition to the idle state
- * firstly the log needs to be idle (no AIL and nothing in the iclogs).
- * If we are then in a state where covering is needed, the caller is informed
- * that dummy transactions are required to move the log into the idle state.
+ * Determine if we have a transaction that has gone to disk that needs to be
+ * covered. To begin the transition to the idle state firstly the log needs to
+ * be idle. That means the CIL, the AIL and the iclogs needs to be empty before
+ * we start attempting to cover the log.
  *
- * Because this is called as part of the sync process, we should also indicate
- * that dummy transactions should be issued in anything but the covered or
- * idle states. This ensures that the log tail is accurately reflected in
- * the log at the end of the sync, hence if a crash occurrs avoids replay
- * of transactions where the metadata is already on disk.
+ * Only if we are then in a state where covering is needed, the caller is
+ * informed that dummy transactions are required to move the log into the idle
+ * state.
+ *
+ * If there are any items in the AIl or CIL, then we do not want to attempt to
+ * cover the log as we may be in a situation where there isn't log space
+ * available to run a dummy transaction and this can lead to deadlocks when the
+ * tail of the log is pinned by an item that is modified in the CIL.  Hence
+ * there's no point in running a dummy transaction at this point because we
+ * can't start trying to idle the log until both the CIL and AIL are empty.
  */
 int
 xfs_log_need_covered(xfs_mount_t *mp)
 {
-       int             needed = 0;
        struct xlog     *log = mp->m_log;
+       int             needed = 0;
 
        if (!xfs_fs_writable(mp))
                return 0;
 
+       if (!xlog_cil_empty(log))
+               return 0;
+
        spin_lock(&log->l_icloglock);
        switch (log->l_covered_state) {
        case XLOG_STATE_COVER_DONE:
@@ -1029,14 +1034,17 @@ xfs_log_need_covered(xfs_mount_t *mp)
                break;
        case XLOG_STATE_COVER_NEED:
        case XLOG_STATE_COVER_NEED2:
-               if (!xfs_ail_min_lsn(log->l_ailp) &&
-                   xlog_iclogs_empty(log)) {
-                       if (log->l_covered_state == XLOG_STATE_COVER_NEED)
-                               log->l_covered_state = XLOG_STATE_COVER_DONE;
-                       else
-                               log->l_covered_state = XLOG_STATE_COVER_DONE2;
-               }
-               /* FALLTHRU */
+               if (xfs_ail_min_lsn(log->l_ailp))
+                       break;
+               if (!xlog_iclogs_empty(log))
+                       break;
+
+               needed = 1;
+               if (log->l_covered_state == XLOG_STATE_COVER_NEED)
+                       log->l_covered_state = XLOG_STATE_COVER_DONE;
+               else
+                       log->l_covered_state = XLOG_STATE_COVER_DONE2;
+               break;
        default:
                needed = 1;
                break;
@@ -1068,6 +1076,7 @@ xlog_assign_tail_lsn_locked(
                tail_lsn = lip->li_lsn;
        else
                tail_lsn = atomic64_read(&log->l_last_sync_lsn);
+       trace_xfs_log_assign_tail_lsn(log, tail_lsn);
        atomic64_set(&log->l_tail_lsn, tail_lsn);
        return tail_lsn;
 }
@@ -1979,7 +1988,7 @@ xlog_print_tic_res(
 
        for (i = 0; i < ticket->t_res_num; i++) {
                uint r_type = ticket->t_res_arr[i].r_type;
-               xfs_warn(mp, "region[%u]: %s - %u bytes\n", i,
+               xfs_warn(mp, "region[%u]: %s - %u bytes", i,
                            ((r_type <= 0 || r_type > XLOG_REG_TYPE_MAX) ?
                            "bad-rtype" : res_type_str[r_type-1]),
                            ticket->t_res_arr[i].r_len);
@@ -3702,11 +3711,9 @@ xlog_verify_iclog(
        /* check validity of iclog pointers */
        spin_lock(&log->l_icloglock);
        icptr = log->l_iclog;
-       for (i=0; i < log->l_iclog_bufs; i++) {
-               if (icptr == NULL)
-                       xfs_emerg(log->l_mp, "%s: invalid ptr", __func__);
-               icptr = icptr->ic_next;
-       }
+       for (i = 0; i < log->l_iclog_bufs; i++, icptr = icptr->ic_next)
+               ASSERT(icptr);
+
        if (icptr != log->l_iclog)
                xfs_emerg(log->l_mp, "%s: corrupt iclog ring", __func__);
        spin_unlock(&log->l_icloglock);
index 1c458487f000a42306cb44f14509d80fea0c2f02..e148719e0a5d59dba022034cd5e988ae47552bde 100644 (file)
@@ -18,8 +18,6 @@
 #ifndef        __XFS_LOG_H__
 #define __XFS_LOG_H__
 
-#include "xfs_log_format.h"
-
 struct xfs_log_vec {
        struct xfs_log_vec      *lv_next;       /* next lv in build list */
        int                     lv_niovecs;     /* number of iovecs in lv */
@@ -82,11 +80,7 @@ struct xlog_ticket;
 struct xfs_log_item;
 struct xfs_item_ops;
 struct xfs_trans;
-
-void   xfs_log_item_init(struct xfs_mount      *mp,
-                       struct xfs_log_item     *item,
-                       int                     type,
-                       const struct xfs_item_ops *ops);
+struct xfs_log_callback;
 
 xfs_lsn_t xfs_log_done(struct xfs_mount *mp,
                       struct xlog_ticket *ticket,
@@ -114,7 +108,7 @@ xfs_lsn_t xlog_assign_tail_lsn_locked(struct xfs_mount *mp);
 void     xfs_log_space_wake(struct xfs_mount *mp);
 int      xfs_log_notify(struct xfs_mount       *mp,
                         struct xlog_in_core    *iclog,
-                        xfs_log_callback_t     *callback_entry);
+                        struct xfs_log_callback *callback_entry);
 int      xfs_log_release_iclog(struct xfs_mount *mp,
                         struct xlog_in_core     *iclog);
 int      xfs_log_reserve(struct xfs_mount *mp,
index cfe97973ba36d1d586c3704b536aebce2e391af1..5eb51fc5eb844f2553e5a2d4711c9a32a74bdd18 100644 (file)
 
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
-#include "xfs_log_priv.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
 #include "xfs_alloc.h"
 #include "xfs_extent_busy.h"
 #include "xfs_discard.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_log.h"
+#include "xfs_log_priv.h"
 
 /*
  * Allocate a new ticket. Failing to get a new ticket makes it really hard to
@@ -711,6 +713,20 @@ xlog_cil_push_foreground(
        xlog_cil_push(log);
 }
 
+bool
+xlog_cil_empty(
+       struct xlog     *log)
+{
+       struct xfs_cil  *cil = log->l_cilp;
+       bool            empty = false;
+
+       spin_lock(&cil->xc_push_lock);
+       if (list_empty(&cil->xc_cil))
+               empty = true;
+       spin_unlock(&cil->xc_push_lock);
+       return empty;
+}
+
 /*
  * Commit a transaction with the given vector to the Committed Item List.
  *
index ca7e28a8ed31d996f7e56862e38af43b449235cc..f0969c77bdbe1ea7d87e732ba27a8c4d3fbd54c4 100644 (file)
@@ -233,178 +233,6 @@ typedef struct xfs_trans_header {
        { XFS_LI_QUOTAOFF,      "XFS_LI_QUOTAOFF" }, \
        { XFS_LI_ICREATE,       "XFS_LI_ICREATE" }
 
-/*
- * Transaction types.  Used to distinguish types of buffers.
- */
-#define XFS_TRANS_SETATTR_NOT_SIZE     1
-#define XFS_TRANS_SETATTR_SIZE         2
-#define XFS_TRANS_INACTIVE             3
-#define XFS_TRANS_CREATE               4
-#define XFS_TRANS_CREATE_TRUNC         5
-#define XFS_TRANS_TRUNCATE_FILE                6
-#define XFS_TRANS_REMOVE               7
-#define XFS_TRANS_LINK                 8
-#define XFS_TRANS_RENAME               9
-#define XFS_TRANS_MKDIR                        10
-#define XFS_TRANS_RMDIR                        11
-#define XFS_TRANS_SYMLINK              12
-#define XFS_TRANS_SET_DMATTRS          13
-#define XFS_TRANS_GROWFS               14
-#define XFS_TRANS_STRAT_WRITE          15
-#define XFS_TRANS_DIOSTRAT             16
-/* 17 was XFS_TRANS_WRITE_SYNC */
-#define        XFS_TRANS_WRITEID               18
-#define        XFS_TRANS_ADDAFORK              19
-#define        XFS_TRANS_ATTRINVAL             20
-#define        XFS_TRANS_ATRUNCATE             21
-#define        XFS_TRANS_ATTR_SET              22
-#define        XFS_TRANS_ATTR_RM               23
-#define        XFS_TRANS_ATTR_FLAG             24
-#define        XFS_TRANS_CLEAR_AGI_BUCKET      25
-#define XFS_TRANS_QM_SBCHANGE          26
-/*
- * Dummy entries since we use the transaction type to index into the
- * trans_type[] in xlog_recover_print_trans_head()
- */
-#define XFS_TRANS_DUMMY1               27
-#define XFS_TRANS_DUMMY2               28
-#define XFS_TRANS_QM_QUOTAOFF          29
-#define XFS_TRANS_QM_DQALLOC           30
-#define XFS_TRANS_QM_SETQLIM           31
-#define XFS_TRANS_QM_DQCLUSTER         32
-#define XFS_TRANS_QM_QINOCREATE                33
-#define XFS_TRANS_QM_QUOTAOFF_END      34
-#define XFS_TRANS_SB_UNIT              35
-#define XFS_TRANS_FSYNC_TS             36
-#define        XFS_TRANS_GROWFSRT_ALLOC        37
-#define        XFS_TRANS_GROWFSRT_ZERO         38
-#define        XFS_TRANS_GROWFSRT_FREE         39
-#define        XFS_TRANS_SWAPEXT               40
-#define        XFS_TRANS_SB_COUNT              41
-#define        XFS_TRANS_CHECKPOINT            42
-#define        XFS_TRANS_ICREATE               43
-#define        XFS_TRANS_TYPE_MAX              43
-/* new transaction types need to be reflected in xfs_logprint(8) */
-
-#define XFS_TRANS_TYPES \
-       { XFS_TRANS_SETATTR_NOT_SIZE,   "SETATTR_NOT_SIZE" }, \
-       { XFS_TRANS_SETATTR_SIZE,       "SETATTR_SIZE" }, \
-       { XFS_TRANS_INACTIVE,           "INACTIVE" }, \
-       { XFS_TRANS_CREATE,             "CREATE" }, \
-       { XFS_TRANS_CREATE_TRUNC,       "CREATE_TRUNC" }, \
-       { XFS_TRANS_TRUNCATE_FILE,      "TRUNCATE_FILE" }, \
-       { XFS_TRANS_REMOVE,             "REMOVE" }, \
-       { XFS_TRANS_LINK,               "LINK" }, \
-       { XFS_TRANS_RENAME,             "RENAME" }, \
-       { XFS_TRANS_MKDIR,              "MKDIR" }, \
-       { XFS_TRANS_RMDIR,              "RMDIR" }, \
-       { XFS_TRANS_SYMLINK,            "SYMLINK" }, \
-       { XFS_TRANS_SET_DMATTRS,        "SET_DMATTRS" }, \
-       { XFS_TRANS_GROWFS,             "GROWFS" }, \
-       { XFS_TRANS_STRAT_WRITE,        "STRAT_WRITE" }, \
-       { XFS_TRANS_DIOSTRAT,           "DIOSTRAT" }, \
-       { XFS_TRANS_WRITEID,            "WRITEID" }, \
-       { XFS_TRANS_ADDAFORK,           "ADDAFORK" }, \
-       { XFS_TRANS_ATTRINVAL,          "ATTRINVAL" }, \
-       { XFS_TRANS_ATRUNCATE,          "ATRUNCATE" }, \
-       { XFS_TRANS_ATTR_SET,           "ATTR_SET" }, \
-       { XFS_TRANS_ATTR_RM,            "ATTR_RM" }, \
-       { XFS_TRANS_ATTR_FLAG,          "ATTR_FLAG" }, \
-       { XFS_TRANS_CLEAR_AGI_BUCKET,   "CLEAR_AGI_BUCKET" }, \
-       { XFS_TRANS_QM_SBCHANGE,        "QM_SBCHANGE" }, \
-       { XFS_TRANS_QM_QUOTAOFF,        "QM_QUOTAOFF" }, \
-       { XFS_TRANS_QM_DQALLOC,         "QM_DQALLOC" }, \
-       { XFS_TRANS_QM_SETQLIM,         "QM_SETQLIM" }, \
-       { XFS_TRANS_QM_DQCLUSTER,       "QM_DQCLUSTER" }, \
-       { XFS_TRANS_QM_QINOCREATE,      "QM_QINOCREATE" }, \
-       { XFS_TRANS_QM_QUOTAOFF_END,    "QM_QOFF_END" }, \
-       { XFS_TRANS_SB_UNIT,            "SB_UNIT" }, \
-       { XFS_TRANS_FSYNC_TS,           "FSYNC_TS" }, \
-       { XFS_TRANS_GROWFSRT_ALLOC,     "GROWFSRT_ALLOC" }, \
-       { XFS_TRANS_GROWFSRT_ZERO,      "GROWFSRT_ZERO" }, \
-       { XFS_TRANS_GROWFSRT_FREE,      "GROWFSRT_FREE" }, \
-       { XFS_TRANS_SWAPEXT,            "SWAPEXT" }, \
-       { XFS_TRANS_SB_COUNT,           "SB_COUNT" }, \
-       { XFS_TRANS_CHECKPOINT,         "CHECKPOINT" }, \
-       { XFS_TRANS_DUMMY1,             "DUMMY1" }, \
-       { XFS_TRANS_DUMMY2,             "DUMMY2" }, \
-       { XLOG_UNMOUNT_REC_TYPE,        "UNMOUNT" }
-
-/*
- * This structure is used to track log items associated with
- * a transaction.  It points to the log item and keeps some
- * flags to track the state of the log item.  It also tracks
- * the amount of space needed to log the item it describes
- * once we get to commit processing (see xfs_trans_commit()).
- */
-struct xfs_log_item_desc {
-       struct xfs_log_item     *lid_item;
-       struct list_head        lid_trans;
-       unsigned char           lid_flags;
-};
-
-#define XFS_LID_DIRTY          0x1
-
-/*
- * Values for t_flags.
- */
-#define        XFS_TRANS_DIRTY         0x01    /* something needs to be logged */
-#define        XFS_TRANS_SB_DIRTY      0x02    /* superblock is modified */
-#define        XFS_TRANS_PERM_LOG_RES  0x04    /* xact took a permanent log res */
-#define        XFS_TRANS_SYNC          0x08    /* make commit synchronous */
-#define XFS_TRANS_DQ_DIRTY     0x10    /* at least one dquot in trx dirty */
-#define XFS_TRANS_RESERVE      0x20    /* OK to use reserved data blocks */
-#define XFS_TRANS_FREEZE_PROT  0x40    /* Transaction has elevated writer
-                                          count in superblock */
-
-/*
- * Values for call flags parameter.
- */
-#define        XFS_TRANS_RELEASE_LOG_RES       0x4
-#define        XFS_TRANS_ABORT                 0x8
-
-/*
- * Field values for xfs_trans_mod_sb.
- */
-#define        XFS_TRANS_SB_ICOUNT             0x00000001
-#define        XFS_TRANS_SB_IFREE              0x00000002
-#define        XFS_TRANS_SB_FDBLOCKS           0x00000004
-#define        XFS_TRANS_SB_RES_FDBLOCKS       0x00000008
-#define        XFS_TRANS_SB_FREXTENTS          0x00000010
-#define        XFS_TRANS_SB_RES_FREXTENTS      0x00000020
-#define        XFS_TRANS_SB_DBLOCKS            0x00000040
-#define        XFS_TRANS_SB_AGCOUNT            0x00000080
-#define        XFS_TRANS_SB_IMAXPCT            0x00000100
-#define        XFS_TRANS_SB_REXTSIZE           0x00000200
-#define        XFS_TRANS_SB_RBMBLOCKS          0x00000400
-#define        XFS_TRANS_SB_RBLOCKS            0x00000800
-#define        XFS_TRANS_SB_REXTENTS           0x00001000
-#define        XFS_TRANS_SB_REXTSLOG           0x00002000
-
-/*
- * Here we centralize the specification of XFS meta-data buffer
- * reference count values.  This determine how hard the buffer
- * cache tries to hold onto the buffer.
- */
-#define        XFS_AGF_REF             4
-#define        XFS_AGI_REF             4
-#define        XFS_AGFL_REF            3
-#define        XFS_INO_BTREE_REF       3
-#define        XFS_ALLOC_BTREE_REF     2
-#define        XFS_BMAP_BTREE_REF      2
-#define        XFS_DIR_BTREE_REF       2
-#define        XFS_INO_REF             2
-#define        XFS_ATTR_BTREE_REF      1
-#define        XFS_DQUOT_REF           1
-
-/*
- * Flags for xfs_trans_ichgtime().
- */
-#define        XFS_ICHGTIME_MOD        0x1     /* data fork modification timestamp */
-#define        XFS_ICHGTIME_CHG        0x2     /* inode field change timestamp */
-#define        XFS_ICHGTIME_CREATE     0x4     /* inode create timestamp */
-
-
 /*
  * Inode Log Item Format definitions.
  *
@@ -797,7 +625,6 @@ typedef struct xfs_qoff_logformat {
        char                    qf_pad[12];     /* padding for future */
 } xfs_qoff_logformat_t;
 
-
 /*
  * Disk quotas status in m_qflags, and also sb_qflags. 16 bits.
  */
@@ -849,8 +676,4 @@ struct xfs_icreate_log {
        __be32          icl_gen;        /* inode generation number to use */
 };
 
-int    xfs_log_calc_unit_res(struct xfs_mount *mp, int unit_bytes);
-int    xfs_log_calc_minimum_size(struct xfs_mount *);
-
-
 #endif /* __XFS_LOG_FORMAT_H__ */
index 136654b9400df9b28a40415b1fbca3c9b7d6a958..9bc403a9e54f300f570e6e200574c36fb46db9b1 100644 (file)
@@ -22,6 +22,7 @@ struct xfs_buf;
 struct xlog;
 struct xlog_ticket;
 struct xfs_mount;
+struct xfs_log_callback;
 
 /*
  * Flags for log structure
@@ -227,8 +228,8 @@ typedef struct xlog_in_core {
 
        /* Callback structures need their own cacheline */
        spinlock_t              ic_callback_lock ____cacheline_aligned_in_smp;
-       xfs_log_callback_t      *ic_callback;
-       xfs_log_callback_t      **ic_callback_tail;
+       struct xfs_log_callback *ic_callback;
+       struct xfs_log_callback **ic_callback_tail;
 
        /* reference counts need their own cacheline */
        atomic_t                ic_refcnt ____cacheline_aligned_in_smp;
@@ -254,7 +255,7 @@ struct xfs_cil_ctx {
        int                     space_used;     /* aggregate size of regions */
        struct list_head        busy_extents;   /* busy extents in chkpt */
        struct xfs_log_vec      *lv_chain;      /* logvecs being pushed */
-       xfs_log_callback_t      log_cb;         /* completion callback hook. */
+       struct xfs_log_callback log_cb;         /* completion callback hook. */
        struct list_head        committing;     /* ctx committing list */
 };
 
@@ -514,12 +515,10 @@ xlog_assign_grant_head(atomic64_t *head, int cycle, int space)
 /*
  * Committed Item List interfaces
  */
-int
-xlog_cil_init(struct xlog *log);
-void
-xlog_cil_init_post_recovery(struct xlog *log);
-void
-xlog_cil_destroy(struct xlog *log);
+int    xlog_cil_init(struct xlog *log);
+void   xlog_cil_init_post_recovery(struct xlog *log);
+void   xlog_cil_destroy(struct xlog *log);
+bool   xlog_cil_empty(struct xlog *log);
 
 /*
  * CIL force routines
index 39797490a1f1996e3f92f51efb532f15a75da8fa..b6b669df40f3ab335e75cd3a67903601be0128a4 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_error.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
-#include "xfs_inode_item.h"
-#include "xfs_alloc.h"
-#include "xfs_ialloc.h"
+#include "xfs_trans.h"
+#include "xfs_log.h"
 #include "xfs_log_priv.h"
-#include "xfs_buf_item.h"
 #include "xfs_log_recover.h"
+#include "xfs_inode_item.h"
 #include "xfs_extfree_item.h"
 #include "xfs_trans_priv.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
 #include "xfs_quota.h"
 #include "xfs_cksum.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
-#include "xfs_icreate_item.h"
-
-/* Need all the magic numbers and buffer ops structures from these headers */
-#include "xfs_symlink.h"
-#include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_dinode.h"
+#include "xfs_error.h"
 #include "xfs_dir2.h"
-#include "xfs_attr_leaf.h"
-#include "xfs_attr_remote.h"
 
 #define BLK_AVG(blk1, blk2)    ((blk1+blk2) >> 1)
 
@@ -305,9 +297,9 @@ xlog_header_check_dump(
        xfs_mount_t             *mp,
        xlog_rec_header_t       *head)
 {
-       xfs_debug(mp, "%s:  SB : uuid = %pU, fmt = %d\n",
+       xfs_debug(mp, "%s:  SB : uuid = %pU, fmt = %d",
                __func__, &mp->m_sb.sb_uuid, XLOG_FMT);
-       xfs_debug(mp, "    log : uuid = %pU, fmt = %d\n",
+       xfs_debug(mp, "    log : uuid = %pU, fmt = %d",
                &head->h_fs_uuid, be32_to_cpu(head->h_fmt));
 }
 #else
@@ -2362,7 +2354,7 @@ xlog_recover_do_reg_buffer(
                                        item->ri_buf[i].i_len, __func__);
                                goto next;
                        }
-                       error = xfs_qm_dqcheck(mp, item->ri_buf[i].i_addr,
+                       error = xfs_dqcheck(mp, item->ri_buf[i].i_addr,
                                               -1, 0, XFS_QMOPT_DOWARN,
                                               "dquot_buf_recover");
                        if (error)
@@ -2393,133 +2385,6 @@ xlog_recover_do_reg_buffer(
                xlog_recover_validate_buf_type(mp, bp, buf_f);
 }
 
-/*
- * Do some primitive error checking on ondisk dquot data structures.
- */
-int
-xfs_qm_dqcheck(
-       struct xfs_mount *mp,
-       xfs_disk_dquot_t *ddq,
-       xfs_dqid_t       id,
-       uint             type,    /* used only when IO_dorepair is true */
-       uint             flags,
-       char             *str)
-{
-       xfs_dqblk_t      *d = (xfs_dqblk_t *)ddq;
-       int             errs = 0;
-
-       /*
-        * We can encounter an uninitialized dquot buffer for 2 reasons:
-        * 1. If we crash while deleting the quotainode(s), and those blks got
-        *    used for user data. This is because we take the path of regular
-        *    file deletion; however, the size field of quotainodes is never
-        *    updated, so all the tricks that we play in itruncate_finish
-        *    don't quite matter.
-        *
-        * 2. We don't play the quota buffers when there's a quotaoff logitem.
-        *    But the allocation will be replayed so we'll end up with an
-        *    uninitialized quota block.
-        *
-        * This is all fine; things are still consistent, and we haven't lost
-        * any quota information. Just don't complain about bad dquot blks.
-        */
-       if (ddq->d_magic != cpu_to_be16(XFS_DQUOT_MAGIC)) {
-               if (flags & XFS_QMOPT_DOWARN)
-                       xfs_alert(mp,
-                       "%s : XFS dquot ID 0x%x, magic 0x%x != 0x%x",
-                       str, id, be16_to_cpu(ddq->d_magic), XFS_DQUOT_MAGIC);
-               errs++;
-       }
-       if (ddq->d_version != XFS_DQUOT_VERSION) {
-               if (flags & XFS_QMOPT_DOWARN)
-                       xfs_alert(mp,
-                       "%s : XFS dquot ID 0x%x, version 0x%x != 0x%x",
-                       str, id, ddq->d_version, XFS_DQUOT_VERSION);
-               errs++;
-       }
-
-       if (ddq->d_flags != XFS_DQ_USER &&
-           ddq->d_flags != XFS_DQ_PROJ &&
-           ddq->d_flags != XFS_DQ_GROUP) {
-               if (flags & XFS_QMOPT_DOWARN)
-                       xfs_alert(mp,
-                       "%s : XFS dquot ID 0x%x, unknown flags 0x%x",
-                       str, id, ddq->d_flags);
-               errs++;
-       }
-
-       if (id != -1 && id != be32_to_cpu(ddq->d_id)) {
-               if (flags & XFS_QMOPT_DOWARN)
-                       xfs_alert(mp,
-                       "%s : ondisk-dquot 0x%p, ID mismatch: "
-                       "0x%x expected, found id 0x%x",
-                       str, ddq, id, be32_to_cpu(ddq->d_id));
-               errs++;
-       }
-
-       if (!errs && ddq->d_id) {
-               if (ddq->d_blk_softlimit &&
-                   be64_to_cpu(ddq->d_bcount) >
-                               be64_to_cpu(ddq->d_blk_softlimit)) {
-                       if (!ddq->d_btimer) {
-                               if (flags & XFS_QMOPT_DOWARN)
-                                       xfs_alert(mp,
-                       "%s : Dquot ID 0x%x (0x%p) BLK TIMER NOT STARTED",
-                                       str, (int)be32_to_cpu(ddq->d_id), ddq);
-                               errs++;
-                       }
-               }
-               if (ddq->d_ino_softlimit &&
-                   be64_to_cpu(ddq->d_icount) >
-                               be64_to_cpu(ddq->d_ino_softlimit)) {
-                       if (!ddq->d_itimer) {
-                               if (flags & XFS_QMOPT_DOWARN)
-                                       xfs_alert(mp,
-                       "%s : Dquot ID 0x%x (0x%p) INODE TIMER NOT STARTED",
-                                       str, (int)be32_to_cpu(ddq->d_id), ddq);
-                               errs++;
-                       }
-               }
-               if (ddq->d_rtb_softlimit &&
-                   be64_to_cpu(ddq->d_rtbcount) >
-                               be64_to_cpu(ddq->d_rtb_softlimit)) {
-                       if (!ddq->d_rtbtimer) {
-                               if (flags & XFS_QMOPT_DOWARN)
-                                       xfs_alert(mp,
-                       "%s : Dquot ID 0x%x (0x%p) RTBLK TIMER NOT STARTED",
-                                       str, (int)be32_to_cpu(ddq->d_id), ddq);
-                               errs++;
-                       }
-               }
-       }
-
-       if (!errs || !(flags & XFS_QMOPT_DQREPAIR))
-               return errs;
-
-       if (flags & XFS_QMOPT_DOWARN)
-               xfs_notice(mp, "Re-initializing dquot ID 0x%x", id);
-
-       /*
-        * Typically, a repair is only requested by quotacheck.
-        */
-       ASSERT(id != -1);
-       ASSERT(flags & XFS_QMOPT_DQREPAIR);
-       memset(d, 0, sizeof(xfs_dqblk_t));
-
-       d->dd_diskdq.d_magic = cpu_to_be16(XFS_DQUOT_MAGIC);
-       d->dd_diskdq.d_version = XFS_DQUOT_VERSION;
-       d->dd_diskdq.d_flags = type;
-       d->dd_diskdq.d_id = cpu_to_be32(id);
-
-       if (xfs_sb_version_hascrc(&mp->m_sb)) {
-               uuid_copy(&d->dd_uuid, &mp->m_sb.sb_uuid);
-               xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk),
-                                XFS_DQUOT_CRC_OFF);
-       }
-
-       return errs;
-}
-
 /*
  * Perform a dquot buffer recovery.
  * Simple algorithm: if we have found a QUOTAOFF log item of the same type
@@ -3125,7 +2990,7 @@ xlog_recover_dquot_pass2(
         */
        dq_f = item->ri_buf[0].i_addr;
        ASSERT(dq_f);
-       error = xfs_qm_dqcheck(mp, recddq, dq_f->qlf_id, 0, XFS_QMOPT_DOWARN,
+       error = xfs_dqcheck(mp, recddq, dq_f->qlf_id, 0, XFS_QMOPT_DOWARN,
                           "xlog_recover_dquot_pass2 (log copy)");
        if (error)
                return XFS_ERROR(EIO);
@@ -3145,7 +3010,7 @@ xlog_recover_dquot_pass2(
         * was among a chunk of dquots created earlier, and we did some
         * minimal initialization then.
         */
-       error = xfs_qm_dqcheck(mp, ddq, dq_f->qlf_id, 0, XFS_QMOPT_DOWARN,
+       error = xfs_dqcheck(mp, ddq, dq_f->qlf_id, 0, XFS_QMOPT_DOWARN,
                           "xlog_recover_dquot_pass2");
        if (error) {
                xfs_buf_relse(bp);
@@ -4077,7 +3942,7 @@ xlog_unpack_data_crc(
        if (crc != rhead->h_crc) {
                if (rhead->h_crc || xfs_sb_version_hascrc(&log->l_mp->m_sb)) {
                        xfs_alert(log->l_mp,
-               "log record CRC mismatch: found 0x%x, expected 0x%x.\n",
+               "log record CRC mismatch: found 0x%x, expected 0x%x.",
                                        le32_to_cpu(rhead->h_crc),
                                        le32_to_cpu(crc));
                        xfs_hex_dump(dp, 32);
index bbcec0bbc12da830f4a16479bfc8748589c4ddbc..2af1a0a4d0f17109ca2b5d1cdf12903c74451100 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_ag.h"
 #include "xfs_sb.h"
 #include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_trans_space.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
 #include "xfs_da_btree.h"
 #include "xfs_attr_leaf.h"
+#include "xfs_bmap_btree.h"
 
 /*
  * Calculate the maximum length in bytes that would be required for a local
index 9163dc14053244c7ff6a70f60e91271995bbe87c..63ca2f0420b108206ed1d610e50fe11f64bf00c4 100644 (file)
@@ -17,9 +17,8 @@
 
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
index 5dcc68019d1bc8c49a799695436045d69d49167f..02df7b408a2623d6a32e7a2bc8285be9348ca646 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
-#include "xfs_dir2.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
-#include "xfs_btree.h"
+#include "xfs_dir2.h"
 #include "xfs_ialloc.h"
 #include "xfs_alloc.h"
 #include "xfs_rtalloc.h"
 #include "xfs_bmap.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_log.h"
 #include "xfs_error.h"
 #include "xfs_quota.h"
 #include "xfs_fsops.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
-#include "xfs_cksum.h"
-#include "xfs_buf_item.h"
+#include "xfs_dinode.h"
 
 
 #ifdef HAVE_PERCPU_SB
@@ -723,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 1fa0584b5627830c77e4bf0fa02db9c55c066a2d..a466c5e5826eed27b489f0018c024362214123c3 100644 (file)
@@ -26,6 +26,7 @@ struct xfs_mru_cache;
 struct xfs_nameops;
 struct xfs_ail;
 struct xfs_quotainfo;
+struct xfs_dir_ops;
 
 #ifdef HAVE_PERCPU_SB
 
@@ -111,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 */
@@ -148,6 +149,8 @@ typedef struct xfs_mount {
        int                     m_dir_magicpct; /* 37% of the dir blocksize */
        __uint8_t               m_sectbb_log;   /* sectlog - BBSHIFT */
        const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */
+       const struct xfs_dir_ops *m_dir_inode_ops; /* vector of dir inode ops */
+       const struct xfs_dir_ops *m_nondir_inode_ops; /* !dir inode ops */
        int                     m_dirblksize;   /* directory block sz--bytes */
        int                     m_dirblkfsbs;   /* directory block sz--fsbs */
        xfs_dablk_t             m_dirdatablk;   /* blockno of dir data v2 */
index 3e6c2e6c9cd24d145514c95b7357ce91acc219f0..14a4996cfec6cb4fbeaa1d3f8432548ebb57d48b 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_ialloc.h"
 #include "xfs_itable.h"
-#include "xfs_rtalloc.h"
+#include "xfs_quota.h"
 #include "xfs_error.h"
 #include "xfs_bmap.h"
-#include "xfs_attr.h"
-#include "xfs_buf_item.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans.h"
 #include "xfs_trans_space.h"
 #include "xfs_qm.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
 #include "xfs_cksum.h"
+#include "xfs_dinode.h"
 
 /*
  * The global quota manager. There is only one of these for the entire
@@ -664,20 +661,6 @@ xfs_qm_dqdetach(
        }
 }
 
-int
-xfs_qm_calc_dquots_per_chunk(
-       struct xfs_mount        *mp,
-       unsigned int            nbblks) /* basic block units */
-{
-       unsigned int    ndquots;
-
-       ASSERT(nbblks > 0);
-       ndquots = BBTOB(nbblks);
-       do_div(ndquots, sizeof(xfs_dqblk_t));
-
-       return ndquots;
-}
-
 struct xfs_qm_isolate {
        struct list_head        buffers;
        struct list_head        dispose;
@@ -858,7 +841,7 @@ xfs_qm_init_quotainfo(
 
        /* Precalc some constants */
        qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
-       qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp,
+       qinf->qi_dqperchunk = xfs_calc_dquots_per_chunk(mp,
                                                        qinf->qi_dqchunklen);
 
        mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);
@@ -1092,10 +1075,10 @@ xfs_qm_reset_dqcounts(
                /*
                 * Do a sanity check, and if needed, repair the dqblk. Don't
                 * output any warnings because it's perfectly possible to
-                * find uninitialised dquot blks. See comment in xfs_qm_dqcheck.
+                * find uninitialised dquot blks. See comment in xfs_dqcheck.
                 */
-               (void) xfs_qm_dqcheck(mp, ddq, id+j, type, XFS_QMOPT_DQREPAIR,
-                                     "xfs_quotacheck");
+               xfs_dqcheck(mp, ddq, id+j, type, XFS_QMOPT_DQREPAIR,
+                           "xfs_quotacheck");
                ddq->d_bcount = 0;
                ddq->d_icount = 0;
                ddq->d_rtbcount = 0;
index 2b602df9c242d3c3efe7b6841abe5fdbee28d00e..a788b66a5cb1c7e73898c877b0c3629eeb8e17a7 100644 (file)
@@ -103,8 +103,6 @@ xfs_dq_to_quota_inode(struct xfs_dquot *dqp)
        return NULL;
 }
 
-extern int     xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp,
-                                            unsigned int nbblks);
 extern void    xfs_trans_mod_dquot(struct xfs_trans *,
                                        struct xfs_dquot *, uint, long);
 extern int     xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
index 3af50ccdfac1a10da858ef26e80b040ad4a474f1..e9be63abd8d29f9003521ce5efa0b2aeaae2f467 100644 (file)
 #include "xfs.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
 #include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
-#include "xfs_itable.h"
-#include "xfs_bmap.h"
-#include "xfs_rtalloc.h"
 #include "xfs_error.h"
-#include "xfs_attr.h"
-#include "xfs_buf_item.h"
+#include "xfs_trans.h"
 #include "xfs_qm.h"
 
 
index 8174aad0b38813ec836ea044d9a1b7b4dc06f300..437c9198031a49a940aecaa441dc305de0e9a145 100644 (file)
 
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
-#include "xfs_inode_item.h"
-#include "xfs_itable.h"
-#include "xfs_bmap.h"
-#include "xfs_rtalloc.h"
+#include "xfs_trans.h"
 #include "xfs_error.h"
-#include "xfs_attr.h"
-#include "xfs_buf_item.h"
+#include "xfs_quota.h"
 #include "xfs_qm.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
@@ -287,7 +281,7 @@ xfs_qm_scall_trunc_qfiles(
        int             error = 0, error2 = 0;
 
        if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0) {
-               xfs_debug(mp, "%s: flags=%x m_qflags=%x\n",
+               xfs_debug(mp, "%s: flags=%x m_qflags=%x",
                        __func__, flags, mp->m_qflags);
                return XFS_ERROR(EINVAL);
        }
@@ -325,7 +319,7 @@ xfs_qm_scall_quotaon(
        sbflags = 0;
 
        if (flags == 0) {
-               xfs_debug(mp, "%s: zero flags, m_qflags=%x\n",
+               xfs_debug(mp, "%s: zero flags, m_qflags=%x",
                        __func__, mp->m_qflags);
                return XFS_ERROR(EINVAL);
        }
@@ -348,7 +342,7 @@ xfs_qm_scall_quotaon(
             (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 &&
             (flags & XFS_PQUOTA_ENFD))) {
                xfs_debug(mp,
-                       "%s: Can't enforce without acct, flags=%x sbflags=%x\n",
+                       "%s: Can't enforce without acct, flags=%x sbflags=%x",
                        __func__, flags, mp->m_sb.sb_qflags);
                return XFS_ERROR(EINVAL);
        }
@@ -648,7 +642,7 @@ xfs_qm_scall_setqlim(
                        q->qi_bsoftlimit = soft;
                }
        } else {
-               xfs_debug(mp, "blkhard %Ld < blksoft %Ld\n", hard, soft);
+               xfs_debug(mp, "blkhard %Ld < blksoft %Ld", hard, soft);
        }
        hard = (newlim->d_fieldmask & FS_DQ_RTBHARD) ?
                (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_hardlimit) :
@@ -664,7 +658,7 @@ xfs_qm_scall_setqlim(
                        q->qi_rtbsoftlimit = soft;
                }
        } else {
-               xfs_debug(mp, "rtbhard %Ld < rtbsoft %Ld\n", hard, soft);
+               xfs_debug(mp, "rtbhard %Ld < rtbsoft %Ld", hard, soft);
        }
 
        hard = (newlim->d_fieldmask & FS_DQ_IHARD) ?
@@ -681,7 +675,7 @@ xfs_qm_scall_setqlim(
                        q->qi_isoftlimit = soft;
                }
        } else {
-               xfs_debug(mp, "ihard %Ld < isoft %Ld\n", hard, soft);
+               xfs_debug(mp, "ihard %Ld < isoft %Ld", hard, soft);
        }
 
        /*
index e7d84d2d86830a25a5cd9778521766d4a592c45b..5376dd406ba2c099e23230014b1b23cbb832f6ad 100644 (file)
@@ -150,10 +150,6 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
        xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, pd, nb, ni, \
                                f | XFS_QMOPT_RES_REGBLKS)
 
-extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
-                               xfs_dqid_t, uint, uint, char *);
 extern int xfs_mount_reset_sbqflags(struct xfs_mount *);
 
-extern const struct xfs_buf_ops xfs_dquot_buf_ops;
-
 #endif /* __XFS_QUOTA_H__ */
index e6b0d6e1f4f2b0560a486e1e14e179193325b71f..b3b2b1065c0f4db8a6c972880b2dc8601e189e03 100644 (file)
@@ -154,4 +154,8 @@ typedef __uint16_t  xfs_qwarncnt_t;
                (XFS_QMOPT_UQUOTA | XFS_QMOPT_PQUOTA | XFS_QMOPT_GQUOTA)
 #define XFS_QMOPT_RESBLK_MASK  (XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_RES_RTBLKS)
 
+extern int xfs_dqcheck(struct xfs_mount *mp, xfs_disk_dquot_t *ddq,
+                      xfs_dqid_t id, uint type, uint flags, char *str);
+extern int xfs_calc_dquots_per_chunk(struct xfs_mount *mp, unsigned int nbblks);
+
 #endif /* __XFS_QUOTA_H__ */
index 1326d81596c2920b27f45021b67b76979e5ef387..af33cafe69b6417c201c90175a8f3e9bb4417814 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
 #include "xfs_trans_resv.h"
-#include "xfs_log.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_inode.h"
 #include "xfs_quota.h"
 #include "xfs_trans.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_inode.h"
 #include "xfs_qm.h"
 #include <linux/quota.h>
 
index 6f9e63c9fc2617ab89966447083527f1d0c94257..a6a76b2b6a85db9ece8acb0565e82e310319ec9d 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_alloc.h"
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
-#include "xfs_rtalloc.h"
-#include "xfs_fsops.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc.h"
 #include "xfs_error.h"
-#include "xfs_inode_item.h"
+#include "xfs_trans.h"
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
 #include "xfs_buf.h"
 #include "xfs_icache.h"
+#include "xfs_dinode.h"
+#include "xfs_rtalloc.h"
 
 
 /*
- * Prototypes for internal functions.
- */
-
-
-STATIC int xfs_rtallocate_range(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
-               xfs_extlen_t, xfs_buf_t **, xfs_fsblock_t *);
-STATIC int xfs_rtany_summary(xfs_mount_t *, xfs_trans_t *, int, int,
-               xfs_rtblock_t, xfs_buf_t **, xfs_fsblock_t *, int *);
-STATIC int xfs_rtcheck_range(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
-               xfs_extlen_t, int, xfs_rtblock_t *, int *);
-STATIC int xfs_rtfind_back(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
-               xfs_rtblock_t, xfs_rtblock_t *);
-STATIC int xfs_rtfind_forw(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
-               xfs_rtblock_t, xfs_rtblock_t *);
-STATIC int xfs_rtget_summary( xfs_mount_t *, xfs_trans_t *, int,
-               xfs_rtblock_t, xfs_buf_t **, xfs_fsblock_t *, xfs_suminfo_t *);
-STATIC int xfs_rtmodify_range(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
-               xfs_extlen_t, int);
-STATIC int xfs_rtmodify_summary(xfs_mount_t *, xfs_trans_t *, int,
-               xfs_rtblock_t, int, xfs_buf_t **, xfs_fsblock_t *);
-
-/*
- * Internal functions.
- */
-
-/*
- * Allocate space to the bitmap or summary file, and zero it, for growfs.
+ * Read and return the summary information for a given extent size,
+ * bitmap block combination.
+ * Keeps track of a current summary block, so we don't keep reading
+ * it from the buffer cache.
  */
 STATIC int                             /* error */
-xfs_growfs_rt_alloc(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_extlen_t    oblocks,        /* old count of blocks */
-       xfs_extlen_t    nblocks,        /* new count of blocks */
-       xfs_inode_t     *ip)            /* inode (bitmap/summary) */
+xfs_rtget_summary(
+       xfs_mount_t     *mp,            /* file system mount structure */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       int             log,            /* log2 of extent size */
+       xfs_rtblock_t   bbno,           /* bitmap block number */
+       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
+       xfs_fsblock_t   *rsb,           /* in/out: summary block number */
+       xfs_suminfo_t   *sum)           /* out: summary info for this block */
 {
-       xfs_fileoff_t   bno;            /* block number in file */
-       xfs_buf_t       *bp;            /* temporary buffer for zeroing */
-       int             committed;      /* transaction committed flag */
-       xfs_daddr_t     d;              /* disk block address */
-       int             error;          /* error return value */
-       xfs_fsblock_t   firstblock;     /* first block allocated in xaction */
-       xfs_bmap_free_t flist;          /* list of freed blocks */
-       xfs_fsblock_t   fsbno;          /* filesystem block for bno */
-       xfs_bmbt_irec_t map;            /* block map output */
-       int             nmap;           /* number of block maps */
-       int             resblks;        /* space reservation */
+       xfs_buf_t       *bp;            /* buffer for summary block */
+       int             error;          /* error value */
+       xfs_fsblock_t   sb;             /* summary fsblock */
+       int             so;             /* index into the summary file */
+       xfs_suminfo_t   *sp;            /* pointer to returned data */
 
        /*
-        * Allocate space to the file, as necessary.
+        * Compute entry number in the summary file.
         */
-       while (oblocks < nblocks) {
-               int             cancelflags = 0;
-               xfs_trans_t     *tp;
-
-               tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ALLOC);
-               resblks = XFS_GROWFSRT_SPACE_RES(mp, nblocks - oblocks);
-               /*
-                * Reserve space & log for one extent added to the file.
-                */
-               error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growdata,
-                                         resblks, 0);
-               if (error)
-                       goto error_cancel;
-               cancelflags = XFS_TRANS_RELEASE_LOG_RES;
-               /*
-                * Lock the inode.
-                */
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
-
-               xfs_bmap_init(&flist, &firstblock);
-               /*
-                * Allocate blocks to the bitmap file.
-                */
-               nmap = 1;
-               cancelflags |= XFS_TRANS_ABORT;
-               error = xfs_bmapi_write(tp, ip, oblocks, nblocks - oblocks,
-                                       XFS_BMAPI_METADATA, &firstblock,
-                                       resblks, &map, &nmap, &flist);
-               if (!error && nmap < 1)
-                       error = XFS_ERROR(ENOSPC);
-               if (error)
-                       goto error_cancel;
-               /*
-                * Free any blocks freed up in the transaction, then commit.
-                */
-               error = xfs_bmap_finish(&tp, &flist, &committed);
-               if (error)
-                       goto error_cancel;
-               error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
-               if (error)
-                       goto error;
+       so = XFS_SUMOFFS(mp, log, bbno);
+       /*
+        * Compute the block number in the summary file.
+        */
+       sb = XFS_SUMOFFSTOBLOCK(mp, so);
+       /*
+        * If we have an old buffer, and the block number matches, use that.
+        */
+       if (rbpp && *rbpp && *rsb == sb)
+               bp = *rbpp;
+       /*
+        * Otherwise we have to get the buffer.
+        */
+       else {
                /*
-                * Now we need to clear the allocated blocks.
-                * Do this one block per transaction, to keep it simple.
+                * If there was an old one, get rid of it first.
                 */
-               cancelflags = 0;
-               for (bno = map.br_startoff, fsbno = map.br_startblock;
-                    bno < map.br_startoff + map.br_blockcount;
-                    bno++, fsbno++) {
-                       tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ZERO);
-                       /*
-                        * Reserve log for one block zeroing.
-                        */
-                       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growrtzero,
-                                                 0, 0);
-                       if (error)
-                               goto error_cancel;
-                       /*
-                        * Lock the bitmap inode.
-                        */
-                       xfs_ilock(ip, XFS_ILOCK_EXCL);
-                       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
-                       /*
-                        * Get a buffer for the block.
-                        */
-                       d = XFS_FSB_TO_DADDR(mp, fsbno);
-                       bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
-                               mp->m_bsize, 0);
-                       if (bp == NULL) {
-                               error = XFS_ERROR(EIO);
-error_cancel:
-                               xfs_trans_cancel(tp, cancelflags);
-                               goto error;
-                       }
-                       memset(bp->b_addr, 0, mp->m_sb.sb_blocksize);
-                       xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
-                       /*
-                        * Commit the transaction.
-                        */
-                       error = xfs_trans_commit(tp, 0);
-                       if (error)
-                               goto error;
+               if (rbpp && *rbpp)
+                       xfs_trans_brelse(tp, *rbpp);
+               error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
+               if (error) {
+                       return error;
                }
                /*
-                * Go on to the next extent, if any.
+                * Remember this buffer and block for the next call.
                 */
-               oblocks = map.br_startoff + map.br_blockcount;
+               if (rbpp) {
+                       *rbpp = bp;
+                       *rsb = sb;
+               }
        }
+       /*
+        * Point to the summary information & copy it out.
+        */
+       sp = XFS_SUMPTR(mp, bp, so);
+       *sum = *sp;
+       /*
+        * Drop the buffer if we're not asked to remember it.
+        */
+       if (!rbpp)
+               xfs_trans_brelse(tp, bp);
        return 0;
-
-error:
-       return error;
 }
 
+
 /*
- * Attempt to allocate an extent minlen<=len<=maxlen starting from
- * bitmap block bbno.  If we don't get maxlen then use prod to trim
- * the length, if given.  Returns error; returns starting block in *rtblock.
- * The lengths are all in rtextents.
+ * Return whether there are any free extents in the size range given
+ * by low and high, for the bitmap block bbno.
  */
 STATIC int                             /* error */
-xfs_rtallocate_extent_block(
-       xfs_mount_t     *mp,            /* file system mount point */
+xfs_rtany_summary(
+       xfs_mount_t     *mp,            /* file system mount structure */
        xfs_trans_t     *tp,            /* transaction pointer */
+       int             low,            /* low log2 extent size */
+       int             high,           /* high log2 extent size */
        xfs_rtblock_t   bbno,           /* bitmap block number */
-       xfs_extlen_t    minlen,         /* minimum length to allocate */
-       xfs_extlen_t    maxlen,         /* maximum length to allocate */
-       xfs_extlen_t    *len,           /* out: actual length allocated */
-       xfs_rtblock_t   *nextp,         /* out: next block to try */
        xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
        xfs_fsblock_t   *rsb,           /* in/out: summary block number */
-       xfs_extlen_t    prod,           /* extent product factor */
-       xfs_rtblock_t   *rtblock)       /* out: start block allocated */
+       int             *stat)          /* out: any good extents here? */
 {
-       xfs_rtblock_t   besti;          /* best rtblock found so far */
-       xfs_rtblock_t   bestlen;        /* best length found so far */
-       xfs_rtblock_t   end;            /* last rtblock in chunk */
        int             error;          /* error value */
-       xfs_rtblock_t   i;              /* current rtblock trying */
-       xfs_rtblock_t   next;           /* next rtblock to try */
-       int             stat;           /* status from internal calls */
+       int             log;            /* loop counter, log2 of ext. size */
+       xfs_suminfo_t   sum;            /* summary data */
 
        /*
-        * Loop over all the extents starting in this bitmap block,
-        * looking for one that's long enough.
+        * Loop over logs of extent sizes.  Order is irrelevant.
         */
-       for (i = XFS_BLOCKTOBIT(mp, bbno), besti = -1, bestlen = 0,
-               end = XFS_BLOCKTOBIT(mp, bbno + 1) - 1;
-            i <= end;
-            i++) {
+       for (log = low; log <= high; log++) {
                /*
-                * See if there's a free extent of maxlen starting at i.
-                * If it's not so then next will contain the first non-free.
+                * Get one summary datum.
                 */
-               error = xfs_rtcheck_range(mp, tp, i, maxlen, 1, &next, &stat);
+               error = xfs_rtget_summary(mp, tp, log, bbno, rbpp, rsb, &sum);
                if (error) {
                        return error;
                }
-               if (stat) {
-                       /*
-                        * i for maxlen is all free, allocate and return that.
-                        */
-                       error = xfs_rtallocate_range(mp, tp, i, maxlen, rbpp,
-                               rsb);
-                       if (error) {
-                               return error;
-                       }
-                       *len = maxlen;
-                       *rtblock = i;
-                       return 0;
-               }
                /*
-                * In the case where we have a variable-sized allocation
-                * request, figure out how big this free piece is,
-                * and if it's big enough for the minimum, and the best
-                * so far, remember it.
+                * If there are any, return success.
                 */
-               if (minlen < maxlen) {
-                       xfs_rtblock_t   thislen;        /* this extent size */
-
-                       thislen = next - i;
-                       if (thislen >= minlen && thislen > bestlen) {
-                               besti = i;
-                               bestlen = thislen;
-                       }
+               if (sum) {
+                       *stat = 1;
+                       return 0;
                }
-               /*
-                * If not done yet, find the start of the next free space.
-                */
-               if (next < end) {
-                       error = xfs_rtfind_forw(mp, tp, next, end, &i);
-                       if (error) {
-                               return error;
-                       }
-               } else
-                       break;
        }
        /*
-        * Searched the whole thing & didn't find a maxlen free extent.
+        * Found nothing, return failure.
+        */
+       *stat = 0;
+       return 0;
+}
+
+
+/*
+ * Copy and transform the summary file, given the old and new
+ * parameters in the mount structures.
+ */
+STATIC int                             /* error */
+xfs_rtcopy_summary(
+       xfs_mount_t     *omp,           /* old file system mount point */
+       xfs_mount_t     *nmp,           /* new file system mount point */
+       xfs_trans_t     *tp)            /* transaction pointer */
+{
+       xfs_rtblock_t   bbno;           /* bitmap block number */
+       xfs_buf_t       *bp;            /* summary buffer */
+       int             error;          /* error return value */
+       int             log;            /* summary level number (log length) */
+       xfs_suminfo_t   sum;            /* summary data */
+       xfs_fsblock_t   sumbno;         /* summary block number */
+
+       bp = NULL;
+       for (log = omp->m_rsumlevels - 1; log >= 0; log--) {
+               for (bbno = omp->m_sb.sb_rbmblocks - 1;
+                    (xfs_srtblock_t)bbno >= 0;
+                    bbno--) {
+                       error = xfs_rtget_summary(omp, tp, log, bbno, &bp,
+                               &sumbno, &sum);
+                       if (error)
+                               return error;
+                       if (sum == 0)
+                               continue;
+                       error = xfs_rtmodify_summary(omp, tp, log, bbno, -sum,
+                               &bp, &sumbno);
+                       if (error)
+                               return error;
+                       error = xfs_rtmodify_summary(nmp, tp, log, bbno, sum,
+                               &bp, &sumbno);
+                       if (error)
+                               return error;
+                       ASSERT(sum > 0);
+               }
+       }
+       return 0;
+}
+/*
+ * Mark an extent specified by start and len allocated.
+ * Updates all the summary information as well as the bitmap.
+ */
+STATIC int                             /* error */
+xfs_rtallocate_range(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   start,          /* start block to allocate */
+       xfs_extlen_t    len,            /* length to allocate */
+       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
+       xfs_fsblock_t   *rsb)           /* in/out: summary block number */
+{
+       xfs_rtblock_t   end;            /* end of the allocated extent */
+       int             error;          /* error value */
+       xfs_rtblock_t   postblock = 0;  /* first block allocated > end */
+       xfs_rtblock_t   preblock = 0;   /* first block allocated < start */
+
+       end = start + len - 1;
+       /*
+        * Assume we're allocating out of the middle of a free extent.
+        * We need to find the beginning and end of the extent so we can
+        * properly update the summary.
+        */
+       error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
+       if (error) {
+               return error;
+       }
+       /*
+        * Find the next allocated block (end of free extent).
+        */
+       error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
+               &postblock);
+       if (error) {
+               return error;
+       }
+       /*
+        * Decrement the summary information corresponding to the entire
+        * (old) free extent.
+        */
+       error = xfs_rtmodify_summary(mp, tp,
+               XFS_RTBLOCKLOG(postblock + 1 - preblock),
+               XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
+       if (error) {
+               return error;
+       }
+       /*
+        * If there are blocks not being allocated at the front of the
+        * old extent, add summary data for them to be free.
+        */
+       if (preblock < start) {
+               error = xfs_rtmodify_summary(mp, tp,
+                       XFS_RTBLOCKLOG(start - preblock),
+                       XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
+               if (error) {
+                       return error;
+               }
+       }
+       /*
+        * If there are blocks not being allocated at the end of the
+        * old extent, add summary data for them to be free.
+        */
+       if (postblock > end) {
+               error = xfs_rtmodify_summary(mp, tp,
+                       XFS_RTBLOCKLOG(postblock - end),
+                       XFS_BITTOBLOCK(mp, end + 1), 1, rbpp, rsb);
+               if (error) {
+                       return error;
+               }
+       }
+       /*
+        * Modify the bitmap to mark this extent allocated.
+        */
+       error = xfs_rtmodify_range(mp, tp, start, len, 0);
+       return error;
+}
+
+/*
+ * Attempt to allocate an extent minlen<=len<=maxlen starting from
+ * bitmap block bbno.  If we don't get maxlen then use prod to trim
+ * the length, if given.  Returns error; returns starting block in *rtblock.
+ * The lengths are all in rtextents.
+ */
+STATIC int                             /* error */
+xfs_rtallocate_extent_block(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   bbno,           /* bitmap block number */
+       xfs_extlen_t    minlen,         /* minimum length to allocate */
+       xfs_extlen_t    maxlen,         /* maximum length to allocate */
+       xfs_extlen_t    *len,           /* out: actual length allocated */
+       xfs_rtblock_t   *nextp,         /* out: next block to try */
+       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
+       xfs_fsblock_t   *rsb,           /* in/out: summary block number */
+       xfs_extlen_t    prod,           /* extent product factor */
+       xfs_rtblock_t   *rtblock)       /* out: start block allocated */
+{
+       xfs_rtblock_t   besti;          /* best rtblock found so far */
+       xfs_rtblock_t   bestlen;        /* best length found so far */
+       xfs_rtblock_t   end;            /* last rtblock in chunk */
+       int             error;          /* error value */
+       xfs_rtblock_t   i;              /* current rtblock trying */
+       xfs_rtblock_t   next;           /* next rtblock to try */
+       int             stat;           /* status from internal calls */
+
+       /*
+        * Loop over all the extents starting in this bitmap block,
+        * looking for one that's long enough.
+        */
+       for (i = XFS_BLOCKTOBIT(mp, bbno), besti = -1, bestlen = 0,
+               end = XFS_BLOCKTOBIT(mp, bbno + 1) - 1;
+            i <= end;
+            i++) {
+               /*
+                * See if there's a free extent of maxlen starting at i.
+                * If it's not so then next will contain the first non-free.
+                */
+               error = xfs_rtcheck_range(mp, tp, i, maxlen, 1, &next, &stat);
+               if (error) {
+                       return error;
+               }
+               if (stat) {
+                       /*
+                        * i for maxlen is all free, allocate and return that.
+                        */
+                       error = xfs_rtallocate_range(mp, tp, i, maxlen, rbpp,
+                               rsb);
+                       if (error) {
+                               return error;
+                       }
+                       *len = maxlen;
+                       *rtblock = i;
+                       return 0;
+               }
+               /*
+                * In the case where we have a variable-sized allocation
+                * request, figure out how big this free piece is,
+                * and if it's big enough for the minimum, and the best
+                * so far, remember it.
+                */
+               if (minlen < maxlen) {
+                       xfs_rtblock_t   thislen;        /* this extent size */
+
+                       thislen = next - i;
+                       if (thislen >= minlen && thislen > bestlen) {
+                               besti = i;
+                               bestlen = thislen;
+                       }
+               }
+               /*
+                * If not done yet, find the start of the next free space.
+                */
+               if (next < end) {
+                       error = xfs_rtfind_forw(mp, tp, next, end, &i);
+                       if (error) {
+                               return error;
+                       }
+               } else
+                       break;
+       }
+       /*
+        * Searched the whole thing & didn't find a maxlen free extent.
         */
        if (minlen < maxlen && besti != -1) {
                xfs_extlen_t    p;      /* amount to trim length by */
@@ -639,1191 +727,205 @@ xfs_rtallocate_extent_size(
                         */
                        if (r != NULLRTBLOCK) {
                                *rtblock = r;
-                               return 0;
-                       }
-                       /*
-                        * If the "next block to try" returned from the
-                        * allocator is beyond the next bitmap block,
-                        * skip to that bitmap block.
-                        */
-                       if (XFS_BITTOBLOCK(mp, n) > i + 1)
-                               i = XFS_BITTOBLOCK(mp, n) - 1;
-               }
-       }
-       /*
-        * Didn't find any maxlen blocks.  Try smaller ones, unless
-        * we're asking for a fixed size extent.
-        */
-       if (minlen > --maxlen) {
-               *rtblock = NULLRTBLOCK;
-               return 0;
-       }
-       ASSERT(minlen != 0);
-       ASSERT(maxlen != 0);
-
-       /*
-        * Loop over sizes, from maxlen down to minlen.
-        * This time, when we do the allocations, allow smaller ones
-        * to succeed.
-        */
-       for (l = xfs_highbit32(maxlen); l >= xfs_highbit32(minlen); l--) {
-               /*
-                * Loop over all the bitmap blocks, try an allocation
-                * starting in that block.
-                */
-               for (i = 0; i < mp->m_sb.sb_rbmblocks; i++) {
-                       /*
-                        * Get the summary information for this level/block.
-                        */
-                       error = xfs_rtget_summary(mp, tp, l, i, rbpp, rsb,
-                                                 &sum);
-                       if (error) {
-                               return error;
-                       }
-                       /*
-                        * If nothing there, go on to next.
-                        */
-                       if (!sum)
-                               continue;
-                       /*
-                        * Try the allocation.  Make sure the specified
-                        * minlen/maxlen are in the possible range for
-                        * this summary level.
-                        */
-                       error = xfs_rtallocate_extent_block(mp, tp, i,
-                                       XFS_RTMAX(minlen, 1 << l),
-                                       XFS_RTMIN(maxlen, (1 << (l + 1)) - 1),
-                                       len, &n, rbpp, rsb, prod, &r);
-                       if (error) {
-                               return error;
-                       }
-                       /*
-                        * If it worked, return that extent.
-                        */
-                       if (r != NULLRTBLOCK) {
-                               *rtblock = r;
-                               return 0;
-                       }
-                       /*
-                        * If the "next block to try" returned from the
-                        * allocator is beyond the next bitmap block,
-                        * skip to that bitmap block.
-                        */
-                       if (XFS_BITTOBLOCK(mp, n) > i + 1)
-                               i = XFS_BITTOBLOCK(mp, n) - 1;
-               }
-       }
-       /*
-        * Got nothing, return failure.
-        */
-       *rtblock = NULLRTBLOCK;
-       return 0;
-}
-
-/*
- * Mark an extent specified by start and len allocated.
- * Updates all the summary information as well as the bitmap.
- */
-STATIC int                             /* error */
-xfs_rtallocate_range(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   start,          /* start block to allocate */
-       xfs_extlen_t    len,            /* length to allocate */
-       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
-       xfs_fsblock_t   *rsb)           /* in/out: summary block number */
-{
-       xfs_rtblock_t   end;            /* end of the allocated extent */
-       int             error;          /* error value */
-       xfs_rtblock_t   postblock = 0;  /* first block allocated > end */
-       xfs_rtblock_t   preblock = 0;   /* first block allocated < start */
-
-       end = start + len - 1;
-       /*
-        * Assume we're allocating out of the middle of a free extent.
-        * We need to find the beginning and end of the extent so we can
-        * properly update the summary.
-        */
-       error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
-       if (error) {
-               return error;
-       }
-       /*
-        * Find the next allocated block (end of free extent).
-        */
-       error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
-               &postblock);
-       if (error) {
-               return error;
-       }
-       /*
-        * Decrement the summary information corresponding to the entire
-        * (old) free extent.
-        */
-       error = xfs_rtmodify_summary(mp, tp,
-               XFS_RTBLOCKLOG(postblock + 1 - preblock),
-               XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
-       if (error) {
-               return error;
-       }
-       /*
-        * If there are blocks not being allocated at the front of the
-        * old extent, add summary data for them to be free.
-        */
-       if (preblock < start) {
-               error = xfs_rtmodify_summary(mp, tp,
-                       XFS_RTBLOCKLOG(start - preblock),
-                       XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
-               if (error) {
-                       return error;
-               }
-       }
-       /*
-        * If there are blocks not being allocated at the end of the
-        * old extent, add summary data for them to be free.
-        */
-       if (postblock > end) {
-               error = xfs_rtmodify_summary(mp, tp,
-                       XFS_RTBLOCKLOG(postblock - end),
-                       XFS_BITTOBLOCK(mp, end + 1), 1, rbpp, rsb);
-               if (error) {
-                       return error;
-               }
-       }
-       /*
-        * Modify the bitmap to mark this extent allocated.
-        */
-       error = xfs_rtmodify_range(mp, tp, start, len, 0);
-       return error;
-}
-
-/*
- * Return whether there are any free extents in the size range given
- * by low and high, for the bitmap block bbno.
- */
-STATIC int                             /* error */
-xfs_rtany_summary(
-       xfs_mount_t     *mp,            /* file system mount structure */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       int             low,            /* low log2 extent size */
-       int             high,           /* high log2 extent size */
-       xfs_rtblock_t   bbno,           /* bitmap block number */
-       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
-       xfs_fsblock_t   *rsb,           /* in/out: summary block number */
-       int             *stat)          /* out: any good extents here? */
-{
-       int             error;          /* error value */
-       int             log;            /* loop counter, log2 of ext. size */
-       xfs_suminfo_t   sum;            /* summary data */
-
-       /*
-        * Loop over logs of extent sizes.  Order is irrelevant.
-        */
-       for (log = low; log <= high; log++) {
-               /*
-                * Get one summary datum.
-                */
-               error = xfs_rtget_summary(mp, tp, log, bbno, rbpp, rsb, &sum);
-               if (error) {
-                       return error;
-               }
-               /*
-                * If there are any, return success.
-                */
-               if (sum) {
-                       *stat = 1;
-                       return 0;
-               }
-       }
-       /*
-        * Found nothing, return failure.
-        */
-       *stat = 0;
-       return 0;
-}
-
-/*
- * Get a buffer for the bitmap or summary file block specified.
- * The buffer is returned read and locked.
- */
-STATIC int                             /* error */
-xfs_rtbuf_get(
-       xfs_mount_t     *mp,            /* file system mount structure */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   block,          /* block number in bitmap or summary */
-       int             issum,          /* is summary not bitmap */
-       xfs_buf_t       **bpp)          /* output: buffer for the block */
-{
-       xfs_buf_t       *bp;            /* block buffer, result */
-       xfs_inode_t     *ip;            /* bitmap or summary inode */
-       xfs_bmbt_irec_t map;
-       int             nmap = 1;
-       int             error;          /* error value */
-
-       ip = issum ? mp->m_rsumip : mp->m_rbmip;
-
-       error = xfs_bmapi_read(ip, block, 1, &map, &nmap, XFS_DATA_FORK);
-       if (error)
-               return error;
-
-       ASSERT(map.br_startblock != NULLFSBLOCK);
-       error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
-                                  XFS_FSB_TO_DADDR(mp, map.br_startblock),
-                                  mp->m_bsize, 0, &bp, NULL);
-       if (error)
-               return error;
-       ASSERT(!xfs_buf_geterror(bp));
-       *bpp = bp;
-       return 0;
-}
-
-#ifdef DEBUG
-/*
- * Check that the given extent (block range) is allocated already.
- */
-STATIC int                             /* error */
-xfs_rtcheck_alloc_range(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   bno,            /* starting block number of extent */
-       xfs_extlen_t    len,            /* length of extent */
-       int             *stat)          /* out: 1 for allocated, 0 for not */
-{
-       xfs_rtblock_t   new;            /* dummy for xfs_rtcheck_range */
-
-       return xfs_rtcheck_range(mp, tp, bno, len, 0, &new, stat);
-}
-#endif
-
-/*
- * Check that the given range is either all allocated (val = 0) or
- * all free (val = 1).
- */
-STATIC int                             /* error */
-xfs_rtcheck_range(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   start,          /* starting block number of extent */
-       xfs_extlen_t    len,            /* length of extent */
-       int             val,            /* 1 for free, 0 for allocated */
-       xfs_rtblock_t   *new,           /* out: first block not matching */
-       int             *stat)          /* out: 1 for matches, 0 for not */
-{
-       xfs_rtword_t    *b;             /* current word in buffer */
-       int             bit;            /* bit number in the word */
-       xfs_rtblock_t   block;          /* bitmap block number */
-       xfs_buf_t       *bp;            /* buf for the block */
-       xfs_rtword_t    *bufp;          /* starting word in buffer */
-       int             error;          /* error value */
-       xfs_rtblock_t   i;              /* current bit number rel. to start */
-       xfs_rtblock_t   lastbit;        /* last useful bit in word */
-       xfs_rtword_t    mask;           /* mask of relevant bits for value */
-       xfs_rtword_t    wdiff;          /* difference from wanted value */
-       int             word;           /* word number in the buffer */
-
-       /*
-        * Compute starting bitmap block number
-        */
-       block = XFS_BITTOBLOCK(mp, start);
-       /*
-        * Read the bitmap block.
-        */
-       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
-       if (error) {
-               return error;
-       }
-       bufp = bp->b_addr;
-       /*
-        * Compute the starting word's address, and starting bit.
-        */
-       word = XFS_BITTOWORD(mp, start);
-       b = &bufp[word];
-       bit = (int)(start & (XFS_NBWORD - 1));
-       /*
-        * 0 (allocated) => all zero's; 1 (free) => all one's.
-        */
-       val = -val;
-       /*
-        * If not starting on a word boundary, deal with the first
-        * (partial) word.
-        */
-       if (bit) {
-               /*
-                * Compute first bit not examined.
-                */
-               lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
-               /*
-                * Mask of relevant bits.
-                */
-               mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = (*b ^ val) & mask)) {
-                       /*
-                        * Different, compute first wrong bit and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i = XFS_RTLOBIT(wdiff) - bit;
-                       *new = start + i;
-                       *stat = 0;
-                       return 0;
-               }
-               i = lastbit - bit;
-               /*
-                * Go on to next block if that's where the next word is
-                * and we need the next word.
-                */
-               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
-                       /*
-                        * If done with this block, get the next one.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
-                       if (error) {
-                               return error;
-                       }
-                       b = bufp = bp->b_addr;
-                       word = 0;
-               } else {
-                       /*
-                        * Go on to the next word in the buffer.
-                        */
-                       b++;
-               }
-       } else {
-               /*
-                * Starting on a word boundary, no partial word.
-                */
-               i = 0;
-       }
-       /*
-        * Loop over whole words in buffers.  When we use up one buffer
-        * we move on to the next one.
-        */
-       while (len - i >= XFS_NBWORD) {
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = *b ^ val)) {
-                       /*
-                        * Different, compute first wrong bit and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i += XFS_RTLOBIT(wdiff);
-                       *new = start + i;
-                       *stat = 0;
-                       return 0;
-               }
-               i += XFS_NBWORD;
-               /*
-                * Go on to next block if that's where the next word is
-                * and we need the next word.
-                */
-               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
-                       /*
-                        * If done with this block, get the next one.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
-                       if (error) {
-                               return error;
-                       }
-                       b = bufp = bp->b_addr;
-                       word = 0;
-               } else {
-                       /*
-                        * Go on to the next word in the buffer.
-                        */
-                       b++;
-               }
-       }
-       /*
-        * If not ending on a word boundary, deal with the last
-        * (partial) word.
-        */
-       if ((lastbit = len - i)) {
-               /*
-                * Mask of relevant bits.
-                */
-               mask = ((xfs_rtword_t)1 << lastbit) - 1;
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = (*b ^ val) & mask)) {
-                       /*
-                        * Different, compute first wrong bit and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i += XFS_RTLOBIT(wdiff);
-                       *new = start + i;
-                       *stat = 0;
-                       return 0;
-               } else
-                       i = len;
-       }
-       /*
-        * Successful, return.
-        */
-       xfs_trans_brelse(tp, bp);
-       *new = start + i;
-       *stat = 1;
-       return 0;
-}
-
-/*
- * Copy and transform the summary file, given the old and new
- * parameters in the mount structures.
- */
-STATIC int                             /* error */
-xfs_rtcopy_summary(
-       xfs_mount_t     *omp,           /* old file system mount point */
-       xfs_mount_t     *nmp,           /* new file system mount point */
-       xfs_trans_t     *tp)            /* transaction pointer */
-{
-       xfs_rtblock_t   bbno;           /* bitmap block number */
-       xfs_buf_t       *bp;            /* summary buffer */
-       int             error;          /* error return value */
-       int             log;            /* summary level number (log length) */
-       xfs_suminfo_t   sum;            /* summary data */
-       xfs_fsblock_t   sumbno;         /* summary block number */
-
-       bp = NULL;
-       for (log = omp->m_rsumlevels - 1; log >= 0; log--) {
-               for (bbno = omp->m_sb.sb_rbmblocks - 1;
-                    (xfs_srtblock_t)bbno >= 0;
-                    bbno--) {
-                       error = xfs_rtget_summary(omp, tp, log, bbno, &bp,
-                               &sumbno, &sum);
-                       if (error)
-                               return error;
-                       if (sum == 0)
-                               continue;
-                       error = xfs_rtmodify_summary(omp, tp, log, bbno, -sum,
-                               &bp, &sumbno);
-                       if (error)
-                               return error;
-                       error = xfs_rtmodify_summary(nmp, tp, log, bbno, sum,
-                               &bp, &sumbno);
-                       if (error)
-                               return error;
-                       ASSERT(sum > 0);
-               }
-       }
-       return 0;
-}
-
-/*
- * Searching backward from start to limit, find the first block whose
- * allocated/free state is different from start's.
- */
-STATIC int                             /* error */
-xfs_rtfind_back(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   start,          /* starting block to look at */
-       xfs_rtblock_t   limit,          /* last block to look at */
-       xfs_rtblock_t   *rtblock)       /* out: start block found */
-{
-       xfs_rtword_t    *b;             /* current word in buffer */
-       int             bit;            /* bit number in the word */
-       xfs_rtblock_t   block;          /* bitmap block number */
-       xfs_buf_t       *bp;            /* buf for the block */
-       xfs_rtword_t    *bufp;          /* starting word in buffer */
-       int             error;          /* error value */
-       xfs_rtblock_t   firstbit;       /* first useful bit in the word */
-       xfs_rtblock_t   i;              /* current bit number rel. to start */
-       xfs_rtblock_t   len;            /* length of inspected area */
-       xfs_rtword_t    mask;           /* mask of relevant bits for value */
-       xfs_rtword_t    want;           /* mask for "good" values */
-       xfs_rtword_t    wdiff;          /* difference from wanted value */
-       int             word;           /* word number in the buffer */
-
-       /*
-        * Compute and read in starting bitmap block for starting block.
-        */
-       block = XFS_BITTOBLOCK(mp, start);
-       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
-       if (error) {
-               return error;
-       }
-       bufp = bp->b_addr;
-       /*
-        * Get the first word's index & point to it.
-        */
-       word = XFS_BITTOWORD(mp, start);
-       b = &bufp[word];
-       bit = (int)(start & (XFS_NBWORD - 1));
-       len = start - limit + 1;
-       /*
-        * Compute match value, based on the bit at start: if 1 (free)
-        * then all-ones, else all-zeroes.
-        */
-       want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
-       /*
-        * If the starting position is not word-aligned, deal with the
-        * partial word.
-        */
-       if (bit < XFS_NBWORD - 1) {
-               /*
-                * Calculate first (leftmost) bit number to look at,
-                * and mask for all the relevant bits in this word.
-                */
-               firstbit = XFS_RTMAX((xfs_srtblock_t)(bit - len + 1), 0);
-               mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
-                       firstbit;
-               /*
-                * Calculate the difference between the value there
-                * and what we're looking for.
-                */
-               if ((wdiff = (*b ^ want) & mask)) {
-                       /*
-                        * Different.  Mark where we are and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i = bit - XFS_RTHIBIT(wdiff);
-                       *rtblock = start - i + 1;
-                       return 0;
-               }
-               i = bit - firstbit + 1;
-               /*
-                * Go on to previous block if that's where the previous word is
-                * and we need the previous word.
-                */
-               if (--word == -1 && i < len) {
-                       /*
-                        * If done with this block, get the previous one.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
-                       if (error) {
-                               return error;
-                       }
-                       bufp = bp->b_addr;
-                       word = XFS_BLOCKWMASK(mp);
-                       b = &bufp[word];
-               } else {
-                       /*
-                        * Go on to the previous word in the buffer.
-                        */
-                       b--;
-               }
-       } else {
-               /*
-                * Starting on a word boundary, no partial word.
-                */
-               i = 0;
-       }
-       /*
-        * Loop over whole words in buffers.  When we use up one buffer
-        * we move on to the previous one.
-        */
-       while (len - i >= XFS_NBWORD) {
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = *b ^ want)) {
-                       /*
-                        * Different, mark where we are and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
-                       *rtblock = start - i + 1;
-                       return 0;
-               }
-               i += XFS_NBWORD;
-               /*
-                * Go on to previous block if that's where the previous word is
-                * and we need the previous word.
-                */
-               if (--word == -1 && i < len) {
-                       /*
-                        * If done with this block, get the previous one.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
-                       if (error) {
-                               return error;
-                       }
-                       bufp = bp->b_addr;
-                       word = XFS_BLOCKWMASK(mp);
-                       b = &bufp[word];
-               } else {
-                       /*
-                        * Go on to the previous word in the buffer.
-                        */
-                       b--;
-               }
-       }
-       /*
-        * If not ending on a word boundary, deal with the last
-        * (partial) word.
-        */
-       if (len - i) {
-               /*
-                * Calculate first (leftmost) bit number to look at,
-                * and mask for all the relevant bits in this word.
-                */
-               firstbit = XFS_NBWORD - (len - i);
-               mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = (*b ^ want) & mask)) {
-                       /*
-                        * Different, mark where we are and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
-                       *rtblock = start - i + 1;
-                       return 0;
-               } else
-                       i = len;
-       }
-       /*
-        * No match, return that we scanned the whole area.
-        */
-       xfs_trans_brelse(tp, bp);
-       *rtblock = start - i + 1;
-       return 0;
-}
-
-/*
- * Searching forward from start to limit, find the first block whose
- * allocated/free state is different from start's.
- */
-STATIC int                             /* error */
-xfs_rtfind_forw(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   start,          /* starting block to look at */
-       xfs_rtblock_t   limit,          /* last block to look at */
-       xfs_rtblock_t   *rtblock)       /* out: start block found */
-{
-       xfs_rtword_t    *b;             /* current word in buffer */
-       int             bit;            /* bit number in the word */
-       xfs_rtblock_t   block;          /* bitmap block number */
-       xfs_buf_t       *bp;            /* buf for the block */
-       xfs_rtword_t    *bufp;          /* starting word in buffer */
-       int             error;          /* error value */
-       xfs_rtblock_t   i;              /* current bit number rel. to start */
-       xfs_rtblock_t   lastbit;        /* last useful bit in the word */
-       xfs_rtblock_t   len;            /* length of inspected area */
-       xfs_rtword_t    mask;           /* mask of relevant bits for value */
-       xfs_rtword_t    want;           /* mask for "good" values */
-       xfs_rtword_t    wdiff;          /* difference from wanted value */
-       int             word;           /* word number in the buffer */
-
-       /*
-        * Compute and read in starting bitmap block for starting block.
-        */
-       block = XFS_BITTOBLOCK(mp, start);
-       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
-       if (error) {
-               return error;
-       }
-       bufp = bp->b_addr;
-       /*
-        * Get the first word's index & point to it.
-        */
-       word = XFS_BITTOWORD(mp, start);
-       b = &bufp[word];
-       bit = (int)(start & (XFS_NBWORD - 1));
-       len = limit - start + 1;
-       /*
-        * Compute match value, based on the bit at start: if 1 (free)
-        * then all-ones, else all-zeroes.
-        */
-       want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
-       /*
-        * If the starting position is not word-aligned, deal with the
-        * partial word.
-        */
-       if (bit) {
-               /*
-                * Calculate last (rightmost) bit number to look at,
-                * and mask for all the relevant bits in this word.
-                */
-               lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
-               mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
-               /*
-                * Calculate the difference between the value there
-                * and what we're looking for.
-                */
-               if ((wdiff = (*b ^ want) & mask)) {
-                       /*
-                        * Different.  Mark where we are and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i = XFS_RTLOBIT(wdiff) - bit;
-                       *rtblock = start + i - 1;
-                       return 0;
-               }
-               i = lastbit - bit;
-               /*
-                * Go on to next block if that's where the next word is
-                * and we need the next word.
-                */
-               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
-                       /*
-                        * If done with this block, get the previous one.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
-                       if (error) {
-                               return error;
-                       }
-                       b = bufp = bp->b_addr;
-                       word = 0;
-               } else {
-                       /*
-                        * Go on to the previous word in the buffer.
-                        */
-                       b++;
-               }
-       } else {
-               /*
-                * Starting on a word boundary, no partial word.
-                */
-               i = 0;
-       }
-       /*
-        * Loop over whole words in buffers.  When we use up one buffer
-        * we move on to the next one.
-        */
-       while (len - i >= XFS_NBWORD) {
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = *b ^ want)) {
-                       /*
-                        * Different, mark where we are and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i += XFS_RTLOBIT(wdiff);
-                       *rtblock = start + i - 1;
-                       return 0;
-               }
-               i += XFS_NBWORD;
-               /*
-                * Go on to next block if that's where the next word is
-                * and we need the next word.
-                */
-               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
-                       /*
-                        * If done with this block, get the next one.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
-                       if (error) {
-                               return error;
-                       }
-                       b = bufp = bp->b_addr;
-                       word = 0;
-               } else {
-                       /*
-                        * Go on to the next word in the buffer.
-                        */
-                       b++;
-               }
-       }
-       /*
-        * If not ending on a word boundary, deal with the last
-        * (partial) word.
-        */
-       if ((lastbit = len - i)) {
-               /*
-                * Calculate mask for all the relevant bits in this word.
-                */
-               mask = ((xfs_rtword_t)1 << lastbit) - 1;
-               /*
-                * Compute difference between actual and desired value.
-                */
-               if ((wdiff = (*b ^ want) & mask)) {
-                       /*
-                        * Different, mark where we are and return.
-                        */
-                       xfs_trans_brelse(tp, bp);
-                       i += XFS_RTLOBIT(wdiff);
-                       *rtblock = start + i - 1;
-                       return 0;
-               } else
-                       i = len;
-       }
-       /*
-        * No match, return that we scanned the whole area.
-        */
-       xfs_trans_brelse(tp, bp);
-       *rtblock = start + i - 1;
-       return 0;
-}
-
-/*
- * Mark an extent specified by start and len freed.
- * Updates all the summary information as well as the bitmap.
- */
-STATIC int                             /* error */
-xfs_rtfree_range(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   start,          /* starting block to free */
-       xfs_extlen_t    len,            /* length to free */
-       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
-       xfs_fsblock_t   *rsb)           /* in/out: summary block number */
-{
-       xfs_rtblock_t   end;            /* end of the freed extent */
-       int             error;          /* error value */
-       xfs_rtblock_t   postblock;      /* first block freed > end */
-       xfs_rtblock_t   preblock;       /* first block freed < start */
-
-       end = start + len - 1;
-       /*
-        * Modify the bitmap to mark this extent freed.
-        */
-       error = xfs_rtmodify_range(mp, tp, start, len, 1);
-       if (error) {
-               return error;
-       }
-       /*
-        * Assume we're freeing out of the middle of an allocated extent.
-        * We need to find the beginning and end of the extent so we can
-        * properly update the summary.
-        */
-       error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
-       if (error) {
-               return error;
-       }
-       /*
-        * Find the next allocated block (end of allocated extent).
-        */
-       error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
-               &postblock);
-       if (error)
-               return error;
-       /*
-        * If there are blocks not being freed at the front of the
-        * old extent, add summary data for them to be allocated.
-        */
-       if (preblock < start) {
-               error = xfs_rtmodify_summary(mp, tp,
-                       XFS_RTBLOCKLOG(start - preblock),
-                       XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
-               if (error) {
-                       return error;
-               }
-       }
-       /*
-        * If there are blocks not being freed at the end of the
-        * old extent, add summary data for them to be allocated.
-        */
-       if (postblock > end) {
-               error = xfs_rtmodify_summary(mp, tp,
-                       XFS_RTBLOCKLOG(postblock - end),
-                       XFS_BITTOBLOCK(mp, end + 1), -1, rbpp, rsb);
-               if (error) {
-                       return error;
-               }
-       }
-       /*
-        * Increment the summary information corresponding to the entire
-        * (new) free extent.
-        */
-       error = xfs_rtmodify_summary(mp, tp,
-               XFS_RTBLOCKLOG(postblock + 1 - preblock),
-               XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
-       return error;
-}
-
-/*
- * Read and return the summary information for a given extent size,
- * bitmap block combination.
- * Keeps track of a current summary block, so we don't keep reading
- * it from the buffer cache.
- */
-STATIC int                             /* error */
-xfs_rtget_summary(
-       xfs_mount_t     *mp,            /* file system mount structure */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       int             log,            /* log2 of extent size */
-       xfs_rtblock_t   bbno,           /* bitmap block number */
-       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
-       xfs_fsblock_t   *rsb,           /* in/out: summary block number */
-       xfs_suminfo_t   *sum)           /* out: summary info for this block */
-{
-       xfs_buf_t       *bp;            /* buffer for summary block */
-       int             error;          /* error value */
-       xfs_fsblock_t   sb;             /* summary fsblock */
-       int             so;             /* index into the summary file */
-       xfs_suminfo_t   *sp;            /* pointer to returned data */
-
-       /*
-        * Compute entry number in the summary file.
-        */
-       so = XFS_SUMOFFS(mp, log, bbno);
-       /*
-        * Compute the block number in the summary file.
-        */
-       sb = XFS_SUMOFFSTOBLOCK(mp, so);
-       /*
-        * If we have an old buffer, and the block number matches, use that.
-        */
-       if (rbpp && *rbpp && *rsb == sb)
-               bp = *rbpp;
-       /*
-        * Otherwise we have to get the buffer.
-        */
-       else {
-               /*
-                * If there was an old one, get rid of it first.
-                */
-               if (rbpp && *rbpp)
-                       xfs_trans_brelse(tp, *rbpp);
-               error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
-               if (error) {
-                       return error;
-               }
-               /*
-                * Remember this buffer and block for the next call.
-                */
-               if (rbpp) {
-                       *rbpp = bp;
-                       *rsb = sb;
-               }
-       }
-       /*
-        * Point to the summary information & copy it out.
-        */
-       sp = XFS_SUMPTR(mp, bp, so);
-       *sum = *sp;
-       /*
-        * Drop the buffer if we're not asked to remember it.
-        */
-       if (!rbpp)
-               xfs_trans_brelse(tp, bp);
-       return 0;
-}
-
-/*
- * Set the given range of bitmap bits to the given value.
- * Do whatever I/O and logging is required.
- */
-STATIC int                             /* error */
-xfs_rtmodify_range(
-       xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   start,          /* starting block to modify */
-       xfs_extlen_t    len,            /* length of extent to modify */
-       int             val)            /* 1 for free, 0 for allocated */
-{
-       xfs_rtword_t    *b;             /* current word in buffer */
-       int             bit;            /* bit number in the word */
-       xfs_rtblock_t   block;          /* bitmap block number */
-       xfs_buf_t       *bp;            /* buf for the block */
-       xfs_rtword_t    *bufp;          /* starting word in buffer */
-       int             error;          /* error value */
-       xfs_rtword_t    *first;         /* first used word in the buffer */
-       int             i;              /* current bit number rel. to start */
-       int             lastbit;        /* last useful bit in word */
-       xfs_rtword_t    mask;           /* mask o frelevant bits for value */
-       int             word;           /* word number in the buffer */
-
-       /*
-        * Compute starting bitmap block number.
-        */
-       block = XFS_BITTOBLOCK(mp, start);
-       /*
-        * Read the bitmap block, and point to its data.
-        */
-       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
-       if (error) {
-               return error;
+                               return 0;
+                       }
+                       /*
+                        * If the "next block to try" returned from the
+                        * allocator is beyond the next bitmap block,
+                        * skip to that bitmap block.
+                        */
+                       if (XFS_BITTOBLOCK(mp, n) > i + 1)
+                               i = XFS_BITTOBLOCK(mp, n) - 1;
+               }
        }
-       bufp = bp->b_addr;
-       /*
-        * Compute the starting word's address, and starting bit.
-        */
-       word = XFS_BITTOWORD(mp, start);
-       first = b = &bufp[word];
-       bit = (int)(start & (XFS_NBWORD - 1));
        /*
-        * 0 (allocated) => all zeroes; 1 (free) => all ones.
+        * Didn't find any maxlen blocks.  Try smaller ones, unless
+        * we're asking for a fixed size extent.
         */
-       val = -val;
+       if (minlen > --maxlen) {
+               *rtblock = NULLRTBLOCK;
+               return 0;
+       }
+       ASSERT(minlen != 0);
+       ASSERT(maxlen != 0);
+
        /*
-        * If not starting on a word boundary, deal with the first
-        * (partial) word.
+        * Loop over sizes, from maxlen down to minlen.
+        * This time, when we do the allocations, allow smaller ones
+        * to succeed.
         */
-       if (bit) {
-               /*
-                * Compute first bit not changed and mask of relevant bits.
-                */
-               lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
-               mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
-               /*
-                * Set/clear the active bits.
-                */
-               if (val)
-                       *b |= mask;
-               else
-                       *b &= ~mask;
-               i = lastbit - bit;
+       for (l = xfs_highbit32(maxlen); l >= xfs_highbit32(minlen); l--) {
                /*
-                * Go on to the next block if that's where the next word is
-                * and we need the next word.
+                * Loop over all the bitmap blocks, try an allocation
+                * starting in that block.
                 */
-               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+               for (i = 0; i < mp->m_sb.sb_rbmblocks; i++) {
                        /*
-                        * Log the changed part of this block.
-                        * Get the next one.
+                        * Get the summary information for this level/block.
                         */
-                       xfs_trans_log_buf(tp, bp,
-                               (uint)((char *)first - (char *)bufp),
-                               (uint)((char *)b - (char *)bufp));
-                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       error = xfs_rtget_summary(mp, tp, l, i, rbpp, rsb,
+                                                 &sum);
                        if (error) {
                                return error;
                        }
-                       first = b = bufp = bp->b_addr;
-                       word = 0;
-               } else {
                        /*
-                        * Go on to the next word in the buffer
+                        * If nothing there, go on to next.
                         */
-                       b++;
-               }
-       } else {
-               /*
-                * Starting on a word boundary, no partial word.
-                */
-               i = 0;
-       }
-       /*
-        * Loop over whole words in buffers.  When we use up one buffer
-        * we move on to the next one.
-        */
-       while (len - i >= XFS_NBWORD) {
-               /*
-                * Set the word value correctly.
-                */
-               *b = val;
-               i += XFS_NBWORD;
-               /*
-                * Go on to the next block if that's where the next word is
-                * and we need the next word.
-                */
-               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       if (!sum)
+                               continue;
                        /*
-                        * Log the changed part of this block.
-                        * Get the next one.
+                        * Try the allocation.  Make sure the specified
+                        * minlen/maxlen are in the possible range for
+                        * this summary level.
                         */
-                       xfs_trans_log_buf(tp, bp,
-                               (uint)((char *)first - (char *)bufp),
-                               (uint)((char *)b - (char *)bufp));
-                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       error = xfs_rtallocate_extent_block(mp, tp, i,
+                                       XFS_RTMAX(minlen, 1 << l),
+                                       XFS_RTMIN(maxlen, (1 << (l + 1)) - 1),
+                                       len, &n, rbpp, rsb, prod, &r);
                        if (error) {
                                return error;
                        }
-                       first = b = bufp = bp->b_addr;
-                       word = 0;
-               } else {
                        /*
-                        * Go on to the next word in the buffer
+                        * If it worked, return that extent.
+                        */
+                       if (r != NULLRTBLOCK) {
+                               *rtblock = r;
+                               return 0;
+                       }
+                       /*
+                        * If the "next block to try" returned from the
+                        * allocator is beyond the next bitmap block,
+                        * skip to that bitmap block.
                         */
-                       b++;
+                       if (XFS_BITTOBLOCK(mp, n) > i + 1)
+                               i = XFS_BITTOBLOCK(mp, n) - 1;
                }
        }
        /*
-        * If not ending on a word boundary, deal with the last
-        * (partial) word.
-        */
-       if ((lastbit = len - i)) {
-               /*
-                * Compute a mask of relevant bits.
-                */
-               bit = 0;
-               mask = ((xfs_rtword_t)1 << lastbit) - 1;
-               /*
-                * Set/clear the active bits.
-                */
-               if (val)
-                       *b |= mask;
-               else
-                       *b &= ~mask;
-               b++;
-       }
-       /*
-        * Log any remaining changed bytes.
+        * Got nothing, return failure.
         */
-       if (b > first)
-               xfs_trans_log_buf(tp, bp, (uint)((char *)first - (char *)bufp),
-                       (uint)((char *)b - (char *)bufp - 1));
+       *rtblock = NULLRTBLOCK;
        return 0;
 }
 
 /*
- * Read and modify the summary information for a given extent size,
- * bitmap block combination.
- * Keeps track of a current summary block, so we don't keep reading
- * it from the buffer cache.
+ * Allocate space to the bitmap or summary file, and zero it, for growfs.
  */
 STATIC int                             /* error */
-xfs_rtmodify_summary(
+xfs_growfs_rt_alloc(
        xfs_mount_t     *mp,            /* file system mount point */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       int             log,            /* log2 of extent size */
-       xfs_rtblock_t   bbno,           /* bitmap block number */
-       int             delta,          /* change to make to summary info */
-       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
-       xfs_fsblock_t   *rsb)           /* in/out: summary block number */
+       xfs_extlen_t    oblocks,        /* old count of blocks */
+       xfs_extlen_t    nblocks,        /* new count of blocks */
+       xfs_inode_t     *ip)            /* inode (bitmap/summary) */
 {
-       xfs_buf_t       *bp;            /* buffer for the summary block */
-       int             error;          /* error value */
-       xfs_fsblock_t   sb;             /* summary fsblock */
-       int             so;             /* index into the summary file */
-       xfs_suminfo_t   *sp;            /* pointer to returned data */
+       xfs_fileoff_t   bno;            /* block number in file */
+       xfs_buf_t       *bp;            /* temporary buffer for zeroing */
+       int             committed;      /* transaction committed flag */
+       xfs_daddr_t     d;              /* disk block address */
+       int             error;          /* error return value */
+       xfs_fsblock_t   firstblock;     /* first block allocated in xaction */
+       xfs_bmap_free_t flist;          /* list of freed blocks */
+       xfs_fsblock_t   fsbno;          /* filesystem block for bno */
+       xfs_bmbt_irec_t map;            /* block map output */
+       int             nmap;           /* number of block maps */
+       int             resblks;        /* space reservation */
 
        /*
-        * Compute entry number in the summary file.
-        */
-       so = XFS_SUMOFFS(mp, log, bbno);
-       /*
-        * Compute the block number in the summary file.
-        */
-       sb = XFS_SUMOFFSTOBLOCK(mp, so);
-       /*
-        * If we have an old buffer, and the block number matches, use that.
-        */
-       if (rbpp && *rbpp && *rsb == sb)
-               bp = *rbpp;
-       /*
-        * Otherwise we have to get the buffer.
+        * Allocate space to the file, as necessary.
         */
-       else {
+       while (oblocks < nblocks) {
+               int             cancelflags = 0;
+               xfs_trans_t     *tp;
+
+               tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ALLOC);
+               resblks = XFS_GROWFSRT_SPACE_RES(mp, nblocks - oblocks);
                /*
-                * If there was an old one, get rid of it first.
+                * Reserve space & log for one extent added to the file.
                 */
-               if (rbpp && *rbpp)
-                       xfs_trans_brelse(tp, *rbpp);
-               error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
-               if (error) {
-                       return error;
-               }
+               error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growdata,
+                                         resblks, 0);
+               if (error)
+                       goto error_cancel;
+               cancelflags = XFS_TRANS_RELEASE_LOG_RES;
                /*
-                * Remember this buffer and block for the next call.
+                * Lock the inode.
                 */
-               if (rbpp) {
-                       *rbpp = bp;
-                       *rsb = sb;
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+               xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+               xfs_bmap_init(&flist, &firstblock);
+               /*
+                * Allocate blocks to the bitmap file.
+                */
+               nmap = 1;
+               cancelflags |= XFS_TRANS_ABORT;
+               error = xfs_bmapi_write(tp, ip, oblocks, nblocks - oblocks,
+                                       XFS_BMAPI_METADATA, &firstblock,
+                                       resblks, &map, &nmap, &flist);
+               if (!error && nmap < 1)
+                       error = XFS_ERROR(ENOSPC);
+               if (error)
+                       goto error_cancel;
+               /*
+                * Free any blocks freed up in the transaction, then commit.
+                */
+               error = xfs_bmap_finish(&tp, &flist, &committed);
+               if (error)
+                       goto error_cancel;
+               error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+               if (error)
+                       goto error;
+               /*
+                * Now we need to clear the allocated blocks.
+                * Do this one block per transaction, to keep it simple.
+                */
+               cancelflags = 0;
+               for (bno = map.br_startoff, fsbno = map.br_startblock;
+                    bno < map.br_startoff + map.br_blockcount;
+                    bno++, fsbno++) {
+                       tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ZERO);
+                       /*
+                        * Reserve log for one block zeroing.
+                        */
+                       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growrtzero,
+                                                 0, 0);
+                       if (error)
+                               goto error_cancel;
+                       /*
+                        * Lock the bitmap inode.
+                        */
+                       xfs_ilock(ip, XFS_ILOCK_EXCL);
+                       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+                       /*
+                        * Get a buffer for the block.
+                        */
+                       d = XFS_FSB_TO_DADDR(mp, fsbno);
+                       bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
+                               mp->m_bsize, 0);
+                       if (bp == NULL) {
+                               error = XFS_ERROR(EIO);
+error_cancel:
+                               xfs_trans_cancel(tp, cancelflags);
+                               goto error;
+                       }
+                       memset(bp->b_addr, 0, mp->m_sb.sb_blocksize);
+                       xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
+                       /*
+                        * Commit the transaction.
+                        */
+                       error = xfs_trans_commit(tp, 0);
+                       if (error)
+                               goto error;
                }
+               /*
+                * Go on to the next extent, if any.
+                */
+               oblocks = map.br_startoff + map.br_blockcount;
        }
-       /*
-        * Point to the summary information, modify and log it.
-        */
-       sp = XFS_SUMPTR(mp, bp, so);
-       *sp += delta;
-       xfs_trans_log_buf(tp, bp, (uint)((char *)sp - (char *)bp->b_addr),
-               (uint)((char *)sp - (char *)bp->b_addr + sizeof(*sp) - 1));
        return 0;
+
+error:
+       return error;
 }
 
 /*
@@ -2128,66 +1230,6 @@ xfs_rtallocate_extent(
        return 0;
 }
 
-/*
- * Free an extent in the realtime subvolume.  Length is expressed in
- * realtime extents, as is the block number.
- */
-int                                    /* error */
-xfs_rtfree_extent(
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_rtblock_t   bno,            /* starting block number to free */
-       xfs_extlen_t    len)            /* length of extent freed */
-{
-       int             error;          /* error value */
-       xfs_mount_t     *mp;            /* file system mount structure */
-       xfs_fsblock_t   sb;             /* summary file block number */
-       xfs_buf_t       *sumbp;         /* summary file block buffer */
-
-       mp = tp->t_mountp;
-
-       ASSERT(mp->m_rbmip->i_itemp != NULL);
-       ASSERT(xfs_isilocked(mp->m_rbmip, XFS_ILOCK_EXCL));
-
-#ifdef DEBUG
-       /*
-        * Check to see that this whole range is currently allocated.
-        */
-       {
-               int     stat;           /* result from checking range */
-
-               error = xfs_rtcheck_alloc_range(mp, tp, bno, len, &stat);
-               if (error) {
-                       return error;
-               }
-               ASSERT(stat);
-       }
-#endif
-       sumbp = NULL;
-       /*
-        * Free the range of realtime blocks.
-        */
-       error = xfs_rtfree_range(mp, tp, bno, len, &sumbp, &sb);
-       if (error) {
-               return error;
-       }
-       /*
-        * Mark more blocks free in the superblock.
-        */
-       xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);
-       /*
-        * If we've now freed all the blocks, reset the file sequence
-        * number to 0.
-        */
-       if (tp->t_frextents_delta + mp->m_sb.sb_frextents ==
-           mp->m_sb.sb_rextents) {
-               if (!(mp->m_rbmip->i_d.di_flags & XFS_DIFLAG_NEWRTBM))
-                       mp->m_rbmip->i_d.di_flags |= XFS_DIFLAG_NEWRTBM;
-               *(__uint64_t *)&mp->m_rbmip->i_d.di_atime = 0;
-               xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
-       }
-       return 0;
-}
-
 /*
  * Initialize realtime fields in the mount structure.
  */
index b2a1a24c0e2f3d8037cdd03f2b8deffc298d38c9..752b63d103003288d48c463571cc59279f8c531d 100644 (file)
@@ -95,6 +95,30 @@ xfs_growfs_rt(
        struct xfs_mount        *mp,    /* file system mount structure */
        xfs_growfs_rt_t         *in);   /* user supplied growfs struct */
 
+/*
+ * From xfs_rtbitmap.c
+ */
+int xfs_rtbuf_get(struct xfs_mount *mp, struct xfs_trans *tp,
+                 xfs_rtblock_t block, int issum, struct xfs_buf **bpp);
+int xfs_rtcheck_range(struct xfs_mount *mp, struct xfs_trans *tp,
+                     xfs_rtblock_t start, xfs_extlen_t len, int val,
+                     xfs_rtblock_t *new, int *stat);
+int xfs_rtfind_back(struct xfs_mount *mp, struct xfs_trans *tp,
+                   xfs_rtblock_t start, xfs_rtblock_t limit,
+                   xfs_rtblock_t *rtblock);
+int xfs_rtfind_forw(struct xfs_mount *mp, struct xfs_trans *tp,
+                   xfs_rtblock_t start, xfs_rtblock_t limit,
+                   xfs_rtblock_t *rtblock);
+int xfs_rtmodify_range(struct xfs_mount *mp, struct xfs_trans *tp,
+                      xfs_rtblock_t start, xfs_extlen_t len, int val);
+int xfs_rtmodify_summary(struct xfs_mount *mp, struct xfs_trans *tp, int log,
+                        xfs_rtblock_t bbno, int delta, xfs_buf_t **rbpp,
+                        xfs_fsblock_t *rsb);
+int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
+                    xfs_rtblock_t start, xfs_extlen_t len,
+                    struct xfs_buf **rbpp, xfs_fsblock_t *rsb);
+
+
 #else
 # define xfs_rtallocate_extent(t,b,min,max,l,a,f,p,rb)  (ENOSYS)
 # define xfs_rtfree_extent(t,b,l)                       (ENOSYS)
diff --git a/fs/xfs/xfs_rtbitmap.c b/fs/xfs/xfs_rtbitmap.c
new file mode 100644 (file)
index 0000000..b1f2fe8
--- /dev/null
@@ -0,0 +1,974 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_util.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+#include "xfs_trans.h"
+#include "xfs_trans_space.h"
+#include "xfs_trace.h"
+#include "xfs_buf.h"
+#include "xfs_icache.h"
+#include "xfs_dinode.h"
+#include "xfs_rtalloc.h"
+
+
+/*
+ * Realtime allocator bitmap functions shared with userspace.
+ */
+
+/*
+ * Get a buffer for the bitmap or summary file block specified.
+ * The buffer is returned read and locked.
+ */
+int
+xfs_rtbuf_get(
+       xfs_mount_t     *mp,            /* file system mount structure */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   block,          /* block number in bitmap or summary */
+       int             issum,          /* is summary not bitmap */
+       xfs_buf_t       **bpp)          /* output: buffer for the block */
+{
+       xfs_buf_t       *bp;            /* block buffer, result */
+       xfs_inode_t     *ip;            /* bitmap or summary inode */
+       xfs_bmbt_irec_t map;
+       int             nmap = 1;
+       int             error;          /* error value */
+
+       ip = issum ? mp->m_rsumip : mp->m_rbmip;
+
+       error = xfs_bmapi_read(ip, block, 1, &map, &nmap, XFS_DATA_FORK);
+       if (error)
+               return error;
+
+       ASSERT(map.br_startblock != NULLFSBLOCK);
+       error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
+                                  XFS_FSB_TO_DADDR(mp, map.br_startblock),
+                                  mp->m_bsize, 0, &bp, NULL);
+       if (error)
+               return error;
+       ASSERT(!xfs_buf_geterror(bp));
+       *bpp = bp;
+       return 0;
+}
+
+/*
+ * Searching backward from start to limit, find the first block whose
+ * allocated/free state is different from start's.
+ */
+int
+xfs_rtfind_back(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   start,          /* starting block to look at */
+       xfs_rtblock_t   limit,          /* last block to look at */
+       xfs_rtblock_t   *rtblock)       /* out: start block found */
+{
+       xfs_rtword_t    *b;             /* current word in buffer */
+       int             bit;            /* bit number in the word */
+       xfs_rtblock_t   block;          /* bitmap block number */
+       xfs_buf_t       *bp;            /* buf for the block */
+       xfs_rtword_t    *bufp;          /* starting word in buffer */
+       int             error;          /* error value */
+       xfs_rtblock_t   firstbit;       /* first useful bit in the word */
+       xfs_rtblock_t   i;              /* current bit number rel. to start */
+       xfs_rtblock_t   len;            /* length of inspected area */
+       xfs_rtword_t    mask;           /* mask of relevant bits for value */
+       xfs_rtword_t    want;           /* mask for "good" values */
+       xfs_rtword_t    wdiff;          /* difference from wanted value */
+       int             word;           /* word number in the buffer */
+
+       /*
+        * Compute and read in starting bitmap block for starting block.
+        */
+       block = XFS_BITTOBLOCK(mp, start);
+       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+       if (error) {
+               return error;
+       }
+       bufp = bp->b_addr;
+       /*
+        * Get the first word's index & point to it.
+        */
+       word = XFS_BITTOWORD(mp, start);
+       b = &bufp[word];
+       bit = (int)(start & (XFS_NBWORD - 1));
+       len = start - limit + 1;
+       /*
+        * Compute match value, based on the bit at start: if 1 (free)
+        * then all-ones, else all-zeroes.
+        */
+       want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
+       /*
+        * If the starting position is not word-aligned, deal with the
+        * partial word.
+        */
+       if (bit < XFS_NBWORD - 1) {
+               /*
+                * Calculate first (leftmost) bit number to look at,
+                * and mask for all the relevant bits in this word.
+                */
+               firstbit = XFS_RTMAX((xfs_srtblock_t)(bit - len + 1), 0);
+               mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
+                       firstbit;
+               /*
+                * Calculate the difference between the value there
+                * and what we're looking for.
+                */
+               if ((wdiff = (*b ^ want) & mask)) {
+                       /*
+                        * Different.  Mark where we are and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i = bit - XFS_RTHIBIT(wdiff);
+                       *rtblock = start - i + 1;
+                       return 0;
+               }
+               i = bit - firstbit + 1;
+               /*
+                * Go on to previous block if that's where the previous word is
+                * and we need the previous word.
+                */
+               if (--word == -1 && i < len) {
+                       /*
+                        * If done with this block, get the previous one.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       bufp = bp->b_addr;
+                       word = XFS_BLOCKWMASK(mp);
+                       b = &bufp[word];
+               } else {
+                       /*
+                        * Go on to the previous word in the buffer.
+                        */
+                       b--;
+               }
+       } else {
+               /*
+                * Starting on a word boundary, no partial word.
+                */
+               i = 0;
+       }
+       /*
+        * Loop over whole words in buffers.  When we use up one buffer
+        * we move on to the previous one.
+        */
+       while (len - i >= XFS_NBWORD) {
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = *b ^ want)) {
+                       /*
+                        * Different, mark where we are and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
+                       *rtblock = start - i + 1;
+                       return 0;
+               }
+               i += XFS_NBWORD;
+               /*
+                * Go on to previous block if that's where the previous word is
+                * and we need the previous word.
+                */
+               if (--word == -1 && i < len) {
+                       /*
+                        * If done with this block, get the previous one.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       bufp = bp->b_addr;
+                       word = XFS_BLOCKWMASK(mp);
+                       b = &bufp[word];
+               } else {
+                       /*
+                        * Go on to the previous word in the buffer.
+                        */
+                       b--;
+               }
+       }
+       /*
+        * If not ending on a word boundary, deal with the last
+        * (partial) word.
+        */
+       if (len - i) {
+               /*
+                * Calculate first (leftmost) bit number to look at,
+                * and mask for all the relevant bits in this word.
+                */
+               firstbit = XFS_NBWORD - (len - i);
+               mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = (*b ^ want) & mask)) {
+                       /*
+                        * Different, mark where we are and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
+                       *rtblock = start - i + 1;
+                       return 0;
+               } else
+                       i = len;
+       }
+       /*
+        * No match, return that we scanned the whole area.
+        */
+       xfs_trans_brelse(tp, bp);
+       *rtblock = start - i + 1;
+       return 0;
+}
+
+/*
+ * Searching forward from start to limit, find the first block whose
+ * allocated/free state is different from start's.
+ */
+int
+xfs_rtfind_forw(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   start,          /* starting block to look at */
+       xfs_rtblock_t   limit,          /* last block to look at */
+       xfs_rtblock_t   *rtblock)       /* out: start block found */
+{
+       xfs_rtword_t    *b;             /* current word in buffer */
+       int             bit;            /* bit number in the word */
+       xfs_rtblock_t   block;          /* bitmap block number */
+       xfs_buf_t       *bp;            /* buf for the block */
+       xfs_rtword_t    *bufp;          /* starting word in buffer */
+       int             error;          /* error value */
+       xfs_rtblock_t   i;              /* current bit number rel. to start */
+       xfs_rtblock_t   lastbit;        /* last useful bit in the word */
+       xfs_rtblock_t   len;            /* length of inspected area */
+       xfs_rtword_t    mask;           /* mask of relevant bits for value */
+       xfs_rtword_t    want;           /* mask for "good" values */
+       xfs_rtword_t    wdiff;          /* difference from wanted value */
+       int             word;           /* word number in the buffer */
+
+       /*
+        * Compute and read in starting bitmap block for starting block.
+        */
+       block = XFS_BITTOBLOCK(mp, start);
+       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+       if (error) {
+               return error;
+       }
+       bufp = bp->b_addr;
+       /*
+        * Get the first word's index & point to it.
+        */
+       word = XFS_BITTOWORD(mp, start);
+       b = &bufp[word];
+       bit = (int)(start & (XFS_NBWORD - 1));
+       len = limit - start + 1;
+       /*
+        * Compute match value, based on the bit at start: if 1 (free)
+        * then all-ones, else all-zeroes.
+        */
+       want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
+       /*
+        * If the starting position is not word-aligned, deal with the
+        * partial word.
+        */
+       if (bit) {
+               /*
+                * Calculate last (rightmost) bit number to look at,
+                * and mask for all the relevant bits in this word.
+                */
+               lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
+               mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
+               /*
+                * Calculate the difference between the value there
+                * and what we're looking for.
+                */
+               if ((wdiff = (*b ^ want) & mask)) {
+                       /*
+                        * Different.  Mark where we are and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i = XFS_RTLOBIT(wdiff) - bit;
+                       *rtblock = start + i - 1;
+                       return 0;
+               }
+               i = lastbit - bit;
+               /*
+                * Go on to next block if that's where the next word is
+                * and we need the next word.
+                */
+               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       /*
+                        * If done with this block, get the previous one.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       b = bufp = bp->b_addr;
+                       word = 0;
+               } else {
+                       /*
+                        * Go on to the previous word in the buffer.
+                        */
+                       b++;
+               }
+       } else {
+               /*
+                * Starting on a word boundary, no partial word.
+                */
+               i = 0;
+       }
+       /*
+        * Loop over whole words in buffers.  When we use up one buffer
+        * we move on to the next one.
+        */
+       while (len - i >= XFS_NBWORD) {
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = *b ^ want)) {
+                       /*
+                        * Different, mark where we are and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i += XFS_RTLOBIT(wdiff);
+                       *rtblock = start + i - 1;
+                       return 0;
+               }
+               i += XFS_NBWORD;
+               /*
+                * Go on to next block if that's where the next word is
+                * and we need the next word.
+                */
+               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       /*
+                        * If done with this block, get the next one.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       b = bufp = bp->b_addr;
+                       word = 0;
+               } else {
+                       /*
+                        * Go on to the next word in the buffer.
+                        */
+                       b++;
+               }
+       }
+       /*
+        * If not ending on a word boundary, deal with the last
+        * (partial) word.
+        */
+       if ((lastbit = len - i)) {
+               /*
+                * Calculate mask for all the relevant bits in this word.
+                */
+               mask = ((xfs_rtword_t)1 << lastbit) - 1;
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = (*b ^ want) & mask)) {
+                       /*
+                        * Different, mark where we are and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i += XFS_RTLOBIT(wdiff);
+                       *rtblock = start + i - 1;
+                       return 0;
+               } else
+                       i = len;
+       }
+       /*
+        * No match, return that we scanned the whole area.
+        */
+       xfs_trans_brelse(tp, bp);
+       *rtblock = start + i - 1;
+       return 0;
+}
+
+/*
+ * Read and modify the summary information for a given extent size,
+ * bitmap block combination.
+ * Keeps track of a current summary block, so we don't keep reading
+ * it from the buffer cache.
+ */
+int
+xfs_rtmodify_summary(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       int             log,            /* log2 of extent size */
+       xfs_rtblock_t   bbno,           /* bitmap block number */
+       int             delta,          /* change to make to summary info */
+       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
+       xfs_fsblock_t   *rsb)           /* in/out: summary block number */
+{
+       xfs_buf_t       *bp;            /* buffer for the summary block */
+       int             error;          /* error value */
+       xfs_fsblock_t   sb;             /* summary fsblock */
+       int             so;             /* index into the summary file */
+       xfs_suminfo_t   *sp;            /* pointer to returned data */
+
+       /*
+        * Compute entry number in the summary file.
+        */
+       so = XFS_SUMOFFS(mp, log, bbno);
+       /*
+        * Compute the block number in the summary file.
+        */
+       sb = XFS_SUMOFFSTOBLOCK(mp, so);
+       /*
+        * If we have an old buffer, and the block number matches, use that.
+        */
+       if (rbpp && *rbpp && *rsb == sb)
+               bp = *rbpp;
+       /*
+        * Otherwise we have to get the buffer.
+        */
+       else {
+               /*
+                * If there was an old one, get rid of it first.
+                */
+               if (rbpp && *rbpp)
+                       xfs_trans_brelse(tp, *rbpp);
+               error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
+               if (error) {
+                       return error;
+               }
+               /*
+                * Remember this buffer and block for the next call.
+                */
+               if (rbpp) {
+                       *rbpp = bp;
+                       *rsb = sb;
+               }
+       }
+       /*
+        * Point to the summary information, modify and log it.
+        */
+       sp = XFS_SUMPTR(mp, bp, so);
+       *sp += delta;
+       xfs_trans_log_buf(tp, bp, (uint)((char *)sp - (char *)bp->b_addr),
+               (uint)((char *)sp - (char *)bp->b_addr + sizeof(*sp) - 1));
+       return 0;
+}
+
+/*
+ * Set the given range of bitmap bits to the given value.
+ * Do whatever I/O and logging is required.
+ */
+int
+xfs_rtmodify_range(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   start,          /* starting block to modify */
+       xfs_extlen_t    len,            /* length of extent to modify */
+       int             val)            /* 1 for free, 0 for allocated */
+{
+       xfs_rtword_t    *b;             /* current word in buffer */
+       int             bit;            /* bit number in the word */
+       xfs_rtblock_t   block;          /* bitmap block number */
+       xfs_buf_t       *bp;            /* buf for the block */
+       xfs_rtword_t    *bufp;          /* starting word in buffer */
+       int             error;          /* error value */
+       xfs_rtword_t    *first;         /* first used word in the buffer */
+       int             i;              /* current bit number rel. to start */
+       int             lastbit;        /* last useful bit in word */
+       xfs_rtword_t    mask;           /* mask o frelevant bits for value */
+       int             word;           /* word number in the buffer */
+
+       /*
+        * Compute starting bitmap block number.
+        */
+       block = XFS_BITTOBLOCK(mp, start);
+       /*
+        * Read the bitmap block, and point to its data.
+        */
+       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+       if (error) {
+               return error;
+       }
+       bufp = bp->b_addr;
+       /*
+        * Compute the starting word's address, and starting bit.
+        */
+       word = XFS_BITTOWORD(mp, start);
+       first = b = &bufp[word];
+       bit = (int)(start & (XFS_NBWORD - 1));
+       /*
+        * 0 (allocated) => all zeroes; 1 (free) => all ones.
+        */
+       val = -val;
+       /*
+        * If not starting on a word boundary, deal with the first
+        * (partial) word.
+        */
+       if (bit) {
+               /*
+                * Compute first bit not changed and mask of relevant bits.
+                */
+               lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
+               mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
+               /*
+                * Set/clear the active bits.
+                */
+               if (val)
+                       *b |= mask;
+               else
+                       *b &= ~mask;
+               i = lastbit - bit;
+               /*
+                * Go on to the next block if that's where the next word is
+                * and we need the next word.
+                */
+               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       /*
+                        * Log the changed part of this block.
+                        * Get the next one.
+                        */
+                       xfs_trans_log_buf(tp, bp,
+                               (uint)((char *)first - (char *)bufp),
+                               (uint)((char *)b - (char *)bufp));
+                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       first = b = bufp = bp->b_addr;
+                       word = 0;
+               } else {
+                       /*
+                        * Go on to the next word in the buffer
+                        */
+                       b++;
+               }
+       } else {
+               /*
+                * Starting on a word boundary, no partial word.
+                */
+               i = 0;
+       }
+       /*
+        * Loop over whole words in buffers.  When we use up one buffer
+        * we move on to the next one.
+        */
+       while (len - i >= XFS_NBWORD) {
+               /*
+                * Set the word value correctly.
+                */
+               *b = val;
+               i += XFS_NBWORD;
+               /*
+                * Go on to the next block if that's where the next word is
+                * and we need the next word.
+                */
+               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       /*
+                        * Log the changed part of this block.
+                        * Get the next one.
+                        */
+                       xfs_trans_log_buf(tp, bp,
+                               (uint)((char *)first - (char *)bufp),
+                               (uint)((char *)b - (char *)bufp));
+                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       first = b = bufp = bp->b_addr;
+                       word = 0;
+               } else {
+                       /*
+                        * Go on to the next word in the buffer
+                        */
+                       b++;
+               }
+       }
+       /*
+        * If not ending on a word boundary, deal with the last
+        * (partial) word.
+        */
+       if ((lastbit = len - i)) {
+               /*
+                * Compute a mask of relevant bits.
+                */
+               bit = 0;
+               mask = ((xfs_rtword_t)1 << lastbit) - 1;
+               /*
+                * Set/clear the active bits.
+                */
+               if (val)
+                       *b |= mask;
+               else
+                       *b &= ~mask;
+               b++;
+       }
+       /*
+        * Log any remaining changed bytes.
+        */
+       if (b > first)
+               xfs_trans_log_buf(tp, bp, (uint)((char *)first - (char *)bufp),
+                       (uint)((char *)b - (char *)bufp - 1));
+       return 0;
+}
+
+/*
+ * Mark an extent specified by start and len freed.
+ * Updates all the summary information as well as the bitmap.
+ */
+int
+xfs_rtfree_range(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   start,          /* starting block to free */
+       xfs_extlen_t    len,            /* length to free */
+       xfs_buf_t       **rbpp,         /* in/out: summary block buffer */
+       xfs_fsblock_t   *rsb)           /* in/out: summary block number */
+{
+       xfs_rtblock_t   end;            /* end of the freed extent */
+       int             error;          /* error value */
+       xfs_rtblock_t   postblock;      /* first block freed > end */
+       xfs_rtblock_t   preblock;       /* first block freed < start */
+
+       end = start + len - 1;
+       /*
+        * Modify the bitmap to mark this extent freed.
+        */
+       error = xfs_rtmodify_range(mp, tp, start, len, 1);
+       if (error) {
+               return error;
+       }
+       /*
+        * Assume we're freeing out of the middle of an allocated extent.
+        * We need to find the beginning and end of the extent so we can
+        * properly update the summary.
+        */
+       error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
+       if (error) {
+               return error;
+       }
+       /*
+        * Find the next allocated block (end of allocated extent).
+        */
+       error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
+               &postblock);
+       if (error)
+               return error;
+       /*
+        * If there are blocks not being freed at the front of the
+        * old extent, add summary data for them to be allocated.
+        */
+       if (preblock < start) {
+               error = xfs_rtmodify_summary(mp, tp,
+                       XFS_RTBLOCKLOG(start - preblock),
+                       XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
+               if (error) {
+                       return error;
+               }
+       }
+       /*
+        * If there are blocks not being freed at the end of the
+        * old extent, add summary data for them to be allocated.
+        */
+       if (postblock > end) {
+               error = xfs_rtmodify_summary(mp, tp,
+                       XFS_RTBLOCKLOG(postblock - end),
+                       XFS_BITTOBLOCK(mp, end + 1), -1, rbpp, rsb);
+               if (error) {
+                       return error;
+               }
+       }
+       /*
+        * Increment the summary information corresponding to the entire
+        * (new) free extent.
+        */
+       error = xfs_rtmodify_summary(mp, tp,
+               XFS_RTBLOCKLOG(postblock + 1 - preblock),
+               XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
+       return error;
+}
+
+/*
+ * Check that the given range is either all allocated (val = 0) or
+ * all free (val = 1).
+ */
+int
+xfs_rtcheck_range(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   start,          /* starting block number of extent */
+       xfs_extlen_t    len,            /* length of extent */
+       int             val,            /* 1 for free, 0 for allocated */
+       xfs_rtblock_t   *new,           /* out: first block not matching */
+       int             *stat)          /* out: 1 for matches, 0 for not */
+{
+       xfs_rtword_t    *b;             /* current word in buffer */
+       int             bit;            /* bit number in the word */
+       xfs_rtblock_t   block;          /* bitmap block number */
+       xfs_buf_t       *bp;            /* buf for the block */
+       xfs_rtword_t    *bufp;          /* starting word in buffer */
+       int             error;          /* error value */
+       xfs_rtblock_t   i;              /* current bit number rel. to start */
+       xfs_rtblock_t   lastbit;        /* last useful bit in word */
+       xfs_rtword_t    mask;           /* mask of relevant bits for value */
+       xfs_rtword_t    wdiff;          /* difference from wanted value */
+       int             word;           /* word number in the buffer */
+
+       /*
+        * Compute starting bitmap block number
+        */
+       block = XFS_BITTOBLOCK(mp, start);
+       /*
+        * Read the bitmap block.
+        */
+       error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+       if (error) {
+               return error;
+       }
+       bufp = bp->b_addr;
+       /*
+        * Compute the starting word's address, and starting bit.
+        */
+       word = XFS_BITTOWORD(mp, start);
+       b = &bufp[word];
+       bit = (int)(start & (XFS_NBWORD - 1));
+       /*
+        * 0 (allocated) => all zero's; 1 (free) => all one's.
+        */
+       val = -val;
+       /*
+        * If not starting on a word boundary, deal with the first
+        * (partial) word.
+        */
+       if (bit) {
+               /*
+                * Compute first bit not examined.
+                */
+               lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
+               /*
+                * Mask of relevant bits.
+                */
+               mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = (*b ^ val) & mask)) {
+                       /*
+                        * Different, compute first wrong bit and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i = XFS_RTLOBIT(wdiff) - bit;
+                       *new = start + i;
+                       *stat = 0;
+                       return 0;
+               }
+               i = lastbit - bit;
+               /*
+                * Go on to next block if that's where the next word is
+                * and we need the next word.
+                */
+               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       /*
+                        * If done with this block, get the next one.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       b = bufp = bp->b_addr;
+                       word = 0;
+               } else {
+                       /*
+                        * Go on to the next word in the buffer.
+                        */
+                       b++;
+               }
+       } else {
+               /*
+                * Starting on a word boundary, no partial word.
+                */
+               i = 0;
+       }
+       /*
+        * Loop over whole words in buffers.  When we use up one buffer
+        * we move on to the next one.
+        */
+       while (len - i >= XFS_NBWORD) {
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = *b ^ val)) {
+                       /*
+                        * Different, compute first wrong bit and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i += XFS_RTLOBIT(wdiff);
+                       *new = start + i;
+                       *stat = 0;
+                       return 0;
+               }
+               i += XFS_NBWORD;
+               /*
+                * Go on to next block if that's where the next word is
+                * and we need the next word.
+                */
+               if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+                       /*
+                        * If done with this block, get the next one.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+                       if (error) {
+                               return error;
+                       }
+                       b = bufp = bp->b_addr;
+                       word = 0;
+               } else {
+                       /*
+                        * Go on to the next word in the buffer.
+                        */
+                       b++;
+               }
+       }
+       /*
+        * If not ending on a word boundary, deal with the last
+        * (partial) word.
+        */
+       if ((lastbit = len - i)) {
+               /*
+                * Mask of relevant bits.
+                */
+               mask = ((xfs_rtword_t)1 << lastbit) - 1;
+               /*
+                * Compute difference between actual and desired value.
+                */
+               if ((wdiff = (*b ^ val) & mask)) {
+                       /*
+                        * Different, compute first wrong bit and return.
+                        */
+                       xfs_trans_brelse(tp, bp);
+                       i += XFS_RTLOBIT(wdiff);
+                       *new = start + i;
+                       *stat = 0;
+                       return 0;
+               } else
+                       i = len;
+       }
+       /*
+        * Successful, return.
+        */
+       xfs_trans_brelse(tp, bp);
+       *new = start + i;
+       *stat = 1;
+       return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Check that the given extent (block range) is allocated already.
+ */
+STATIC int                             /* error */
+xfs_rtcheck_alloc_range(
+       xfs_mount_t     *mp,            /* file system mount point */
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   bno,            /* starting block number of extent */
+       xfs_extlen_t    len)            /* length of extent */
+{
+       xfs_rtblock_t   new;            /* dummy for xfs_rtcheck_range */
+       int             stat;
+       int             error;
+
+       error = xfs_rtcheck_range(mp, tp, bno, len, 0, &new, &stat);
+       if (error)
+               return error;
+       ASSERT(stat);
+       return 0;
+}
+#else
+#define xfs_rtcheck_alloc_range(m,t,b,l)       (0)
+#endif
+/*
+ * Free an extent in the realtime subvolume.  Length is expressed in
+ * realtime extents, as is the block number.
+ */
+int                                    /* error */
+xfs_rtfree_extent(
+       xfs_trans_t     *tp,            /* transaction pointer */
+       xfs_rtblock_t   bno,            /* starting block number to free */
+       xfs_extlen_t    len)            /* length of extent freed */
+{
+       int             error;          /* error value */
+       xfs_mount_t     *mp;            /* file system mount structure */
+       xfs_fsblock_t   sb;             /* summary file block number */
+       xfs_buf_t       *sumbp = NULL;  /* summary file block buffer */
+
+       mp = tp->t_mountp;
+
+       ASSERT(mp->m_rbmip->i_itemp != NULL);
+       ASSERT(xfs_isilocked(mp->m_rbmip, XFS_ILOCK_EXCL));
+
+       error = xfs_rtcheck_alloc_range(mp, tp, bno, len);
+       if (error)
+               return error;
+
+       /*
+        * Free the range of realtime blocks.
+        */
+       error = xfs_rtfree_range(mp, tp, bno, len, &sumbp, &sb);
+       if (error) {
+               return error;
+       }
+       /*
+        * Mark more blocks free in the superblock.
+        */
+       xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);
+       /*
+        * If we've now freed all the blocks, reset the file sequence
+        * number to 0.
+        */
+       if (tp->t_frextents_delta + mp->m_sb.sb_frextents ==
+           mp->m_sb.sb_rextents) {
+               if (!(mp->m_rbmip->i_d.di_flags & XFS_DIFLAG_NEWRTBM))
+                       mp->m_rbmip->i_d.di_flags |= XFS_DIFLAG_NEWRTBM;
+               *(__uint64_t *)&mp->m_rbmip->i_d.di_atime = 0;
+               xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
+       }
+       return 0;
+}
+
index a5b59d92eb7095cca4039dbfb2ba5c398ea8fbe2..b7c9aea77f8fd2e7ca88537df77536eca8148418 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_trans_priv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
-#include "xfs_dir2.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_btree.h"
 #include "xfs_ialloc.h"
 #include "xfs_alloc.h"
-#include "xfs_rtalloc.h"
-#include "xfs_bmap.h"
 #include "xfs_error.h"
-#include "xfs_quota.h"
-#include "xfs_fsops.h"
 #include "xfs_trace.h"
 #include "xfs_cksum.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
+#include "xfs_dinode.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
 
 /*
  * Physical superblock buffer manipulations. Shared with libxfs in userspace.
@@ -249,13 +241,13 @@ xfs_mount_validate_sb(
        if (xfs_sb_version_has_pquotino(sbp)) {
                if (sbp->sb_qflags & (XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD)) {
                        xfs_notice(mp,
-                          "Version 5 of Super block has XFS_OQUOTA bits.\n");
+                          "Version 5 of Super block has XFS_OQUOTA bits.");
                        return XFS_ERROR(EFSCORRUPTED);
                }
        } else if (sbp->sb_qflags & (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD |
                                XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD)) {
                        xfs_notice(mp,
-"Superblock earlier than Version 5 has XFS_[PQ]UOTA_{ENFD|CHKD} bits.\n");
+"Superblock earlier than Version 5 has XFS_[PQ]UOTA_{ENFD|CHKD} bits.");
                        return XFS_ERROR(EFSCORRUPTED);
        }
 
@@ -596,6 +588,11 @@ xfs_sb_verify(
  * single bit error could clear the feature bit and unused parts of the
  * superblock are supposed to be zero. Hence a non-null crc field indicates that
  * we've potentially lost a feature bit and we should check it anyway.
+ *
+ * However, past bugs (i.e. in growfs) left non-zeroed regions beyond the
+ * last field in V4 secondary superblocks.  So for secondary superblocks,
+ * we are more forgiving, and ignore CRC failures if the primary doesn't
+ * indicate that the fs version is V5.
  */
 static void
 xfs_sb_read_verify(
@@ -616,16 +613,21 @@ xfs_sb_read_verify(
 
                if (!xfs_verify_cksum(bp->b_addr, be16_to_cpu(dsb->sb_sectsize),
                                      offsetof(struct xfs_sb, sb_crc))) {
-                       error = EFSCORRUPTED;
-                       goto out_error;
+                       /* Only fail bad secondaries on a known V5 filesystem */
+                       if (bp->b_bn != XFS_SB_DADDR &&
+                           xfs_sb_version_hascrc(&mp->m_sb)) {
+                               error = EFSCORRUPTED;
+                               goto out_error;
+                       }
                }
        }
        error = xfs_sb_verify(bp, true);
 
 out_error:
        if (error) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
-                                    mp, bp->b_addr);
+               if (error != EWRONGFS)
+                       XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
+                                            mp, bp->b_addr);
                xfs_buf_ioerror(bp, error);
        }
 }
index 6835b44f850e58e780c2712e24530dbb3e57f5f4..35061d4b614c7ab9fabb80e1b93ffb6bc8b586d2 100644 (file)
@@ -699,7 +699,4 @@ extern void xfs_sb_from_disk(struct xfs_sb *, struct xfs_dsb *);
 extern void    xfs_sb_to_disk(struct xfs_dsb *, struct xfs_sb *, __int64_t);
 extern void    xfs_sb_quota_from_disk(struct xfs_sb *sbp);
 
-extern const struct xfs_buf_ops xfs_sb_buf_ops;
-extern const struct xfs_buf_ops xfs_sb_quiet_buf_ops;
-
 #endif /* __XFS_SB_H__ */
diff --git a/fs/xfs/xfs_shared.h b/fs/xfs/xfs_shared.h
new file mode 100644 (file)
index 0000000..8c5035a
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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 the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __XFS_SHARED_H__
+#define __XFS_SHARED_H__
+
+/*
+ * Definitions shared between kernel and userspace that don't fit into any other
+ * header file that is shared with userspace.
+ */
+struct xfs_ifork;
+struct xfs_buf;
+struct xfs_buf_ops;
+struct xfs_mount;
+struct xfs_trans;
+struct xfs_inode;
+
+/*
+ * Buffer verifier operations are widely used, including userspace tools
+ */
+extern const struct xfs_buf_ops xfs_agf_buf_ops;
+extern const struct xfs_buf_ops xfs_agi_buf_ops;
+extern const struct xfs_buf_ops xfs_agf_buf_ops;
+extern const struct xfs_buf_ops xfs_agfl_buf_ops;
+extern const struct xfs_buf_ops xfs_allocbt_buf_ops;
+extern const struct xfs_buf_ops xfs_attr3_leaf_buf_ops;
+extern const struct xfs_buf_ops xfs_attr3_rmt_buf_ops;
+extern const struct xfs_buf_ops xfs_bmbt_buf_ops;
+extern const struct xfs_buf_ops xfs_da3_node_buf_ops;
+extern const struct xfs_buf_ops xfs_dquot_buf_ops;
+extern const struct xfs_buf_ops xfs_symlink_buf_ops;
+extern const struct xfs_buf_ops xfs_agi_buf_ops;
+extern const struct xfs_buf_ops xfs_inobt_buf_ops;
+extern const struct xfs_buf_ops xfs_inode_buf_ops;
+extern const struct xfs_buf_ops xfs_inode_buf_ra_ops;
+extern const struct xfs_buf_ops xfs_dquot_buf_ops;
+extern const struct xfs_buf_ops xfs_sb_buf_ops;
+extern const struct xfs_buf_ops xfs_sb_quiet_buf_ops;
+extern const struct xfs_buf_ops xfs_symlink_buf_ops;
+
+/*
+ * Transaction types.  Used to distinguish types of buffers. These never reach
+ * the log.
+ */
+#define XFS_TRANS_SETATTR_NOT_SIZE     1
+#define XFS_TRANS_SETATTR_SIZE         2
+#define XFS_TRANS_INACTIVE             3
+#define XFS_TRANS_CREATE               4
+#define XFS_TRANS_CREATE_TRUNC         5
+#define XFS_TRANS_TRUNCATE_FILE                6
+#define XFS_TRANS_REMOVE               7
+#define XFS_TRANS_LINK                 8
+#define XFS_TRANS_RENAME               9
+#define XFS_TRANS_MKDIR                        10
+#define XFS_TRANS_RMDIR                        11
+#define XFS_TRANS_SYMLINK              12
+#define XFS_TRANS_SET_DMATTRS          13
+#define XFS_TRANS_GROWFS               14
+#define XFS_TRANS_STRAT_WRITE          15
+#define XFS_TRANS_DIOSTRAT             16
+/* 17 was XFS_TRANS_WRITE_SYNC */
+#define        XFS_TRANS_WRITEID               18
+#define        XFS_TRANS_ADDAFORK              19
+#define        XFS_TRANS_ATTRINVAL             20
+#define        XFS_TRANS_ATRUNCATE             21
+#define        XFS_TRANS_ATTR_SET              22
+#define        XFS_TRANS_ATTR_RM               23
+#define        XFS_TRANS_ATTR_FLAG             24
+#define        XFS_TRANS_CLEAR_AGI_BUCKET      25
+#define XFS_TRANS_QM_SBCHANGE          26
+/*
+ * Dummy entries since we use the transaction type to index into the
+ * trans_type[] in xlog_recover_print_trans_head()
+ */
+#define XFS_TRANS_DUMMY1               27
+#define XFS_TRANS_DUMMY2               28
+#define XFS_TRANS_QM_QUOTAOFF          29
+#define XFS_TRANS_QM_DQALLOC           30
+#define XFS_TRANS_QM_SETQLIM           31
+#define XFS_TRANS_QM_DQCLUSTER         32
+#define XFS_TRANS_QM_QINOCREATE                33
+#define XFS_TRANS_QM_QUOTAOFF_END      34
+#define XFS_TRANS_SB_UNIT              35
+#define XFS_TRANS_FSYNC_TS             36
+#define        XFS_TRANS_GROWFSRT_ALLOC        37
+#define        XFS_TRANS_GROWFSRT_ZERO         38
+#define        XFS_TRANS_GROWFSRT_FREE         39
+#define        XFS_TRANS_SWAPEXT               40
+#define        XFS_TRANS_SB_COUNT              41
+#define        XFS_TRANS_CHECKPOINT            42
+#define        XFS_TRANS_ICREATE               43
+#define        XFS_TRANS_TYPE_MAX              43
+/* new transaction types need to be reflected in xfs_logprint(8) */
+
+#define XFS_TRANS_TYPES \
+       { XFS_TRANS_SETATTR_NOT_SIZE,   "SETATTR_NOT_SIZE" }, \
+       { XFS_TRANS_SETATTR_SIZE,       "SETATTR_SIZE" }, \
+       { XFS_TRANS_INACTIVE,           "INACTIVE" }, \
+       { XFS_TRANS_CREATE,             "CREATE" }, \
+       { XFS_TRANS_CREATE_TRUNC,       "CREATE_TRUNC" }, \
+       { XFS_TRANS_TRUNCATE_FILE,      "TRUNCATE_FILE" }, \
+       { XFS_TRANS_REMOVE,             "REMOVE" }, \
+       { XFS_TRANS_LINK,               "LINK" }, \
+       { XFS_TRANS_RENAME,             "RENAME" }, \
+       { XFS_TRANS_MKDIR,              "MKDIR" }, \
+       { XFS_TRANS_RMDIR,              "RMDIR" }, \
+       { XFS_TRANS_SYMLINK,            "SYMLINK" }, \
+       { XFS_TRANS_SET_DMATTRS,        "SET_DMATTRS" }, \
+       { XFS_TRANS_GROWFS,             "GROWFS" }, \
+       { XFS_TRANS_STRAT_WRITE,        "STRAT_WRITE" }, \
+       { XFS_TRANS_DIOSTRAT,           "DIOSTRAT" }, \
+       { XFS_TRANS_WRITEID,            "WRITEID" }, \
+       { XFS_TRANS_ADDAFORK,           "ADDAFORK" }, \
+       { XFS_TRANS_ATTRINVAL,          "ATTRINVAL" }, \
+       { XFS_TRANS_ATRUNCATE,          "ATRUNCATE" }, \
+       { XFS_TRANS_ATTR_SET,           "ATTR_SET" }, \
+       { XFS_TRANS_ATTR_RM,            "ATTR_RM" }, \
+       { XFS_TRANS_ATTR_FLAG,          "ATTR_FLAG" }, \
+       { XFS_TRANS_CLEAR_AGI_BUCKET,   "CLEAR_AGI_BUCKET" }, \
+       { XFS_TRANS_QM_SBCHANGE,        "QM_SBCHANGE" }, \
+       { XFS_TRANS_QM_QUOTAOFF,        "QM_QUOTAOFF" }, \
+       { XFS_TRANS_QM_DQALLOC,         "QM_DQALLOC" }, \
+       { XFS_TRANS_QM_SETQLIM,         "QM_SETQLIM" }, \
+       { XFS_TRANS_QM_DQCLUSTER,       "QM_DQCLUSTER" }, \
+       { XFS_TRANS_QM_QINOCREATE,      "QM_QINOCREATE" }, \
+       { XFS_TRANS_QM_QUOTAOFF_END,    "QM_QOFF_END" }, \
+       { XFS_TRANS_SB_UNIT,            "SB_UNIT" }, \
+       { XFS_TRANS_FSYNC_TS,           "FSYNC_TS" }, \
+       { XFS_TRANS_GROWFSRT_ALLOC,     "GROWFSRT_ALLOC" }, \
+       { XFS_TRANS_GROWFSRT_ZERO,      "GROWFSRT_ZERO" }, \
+       { XFS_TRANS_GROWFSRT_FREE,      "GROWFSRT_FREE" }, \
+       { XFS_TRANS_SWAPEXT,            "SWAPEXT" }, \
+       { XFS_TRANS_SB_COUNT,           "SB_COUNT" }, \
+       { XFS_TRANS_CHECKPOINT,         "CHECKPOINT" }, \
+       { XFS_TRANS_DUMMY1,             "DUMMY1" }, \
+       { XFS_TRANS_DUMMY2,             "DUMMY2" }, \
+       { XLOG_UNMOUNT_REC_TYPE,        "UNMOUNT" }
+
+/*
+ * This structure is used to track log items associated with
+ * a transaction.  It points to the log item and keeps some
+ * flags to track the state of the log item.  It also tracks
+ * the amount of space needed to log the item it describes
+ * once we get to commit processing (see xfs_trans_commit()).
+ */
+struct xfs_log_item_desc {
+       struct xfs_log_item     *lid_item;
+       struct list_head        lid_trans;
+       unsigned char           lid_flags;
+};
+
+#define XFS_LID_DIRTY          0x1
+
+/* log size calculation functions */
+int    xfs_log_calc_unit_res(struct xfs_mount *mp, int unit_bytes);
+int    xfs_log_calc_minimum_size(struct xfs_mount *);
+
+
+/*
+ * Values for t_flags.
+ */
+#define        XFS_TRANS_DIRTY         0x01    /* something needs to be logged */
+#define        XFS_TRANS_SB_DIRTY      0x02    /* superblock is modified */
+#define        XFS_TRANS_PERM_LOG_RES  0x04    /* xact took a permanent log res */
+#define        XFS_TRANS_SYNC          0x08    /* make commit synchronous */
+#define XFS_TRANS_DQ_DIRTY     0x10    /* at least one dquot in trx dirty */
+#define XFS_TRANS_RESERVE      0x20    /* OK to use reserved data blocks */
+#define XFS_TRANS_FREEZE_PROT  0x40    /* Transaction has elevated writer
+                                          count in superblock */
+/*
+ * Values for call flags parameter.
+ */
+#define        XFS_TRANS_RELEASE_LOG_RES       0x4
+#define        XFS_TRANS_ABORT                 0x8
+
+/*
+ * Field values for xfs_trans_mod_sb.
+ */
+#define        XFS_TRANS_SB_ICOUNT             0x00000001
+#define        XFS_TRANS_SB_IFREE              0x00000002
+#define        XFS_TRANS_SB_FDBLOCKS           0x00000004
+#define        XFS_TRANS_SB_RES_FDBLOCKS       0x00000008
+#define        XFS_TRANS_SB_FREXTENTS          0x00000010
+#define        XFS_TRANS_SB_RES_FREXTENTS      0x00000020
+#define        XFS_TRANS_SB_DBLOCKS            0x00000040
+#define        XFS_TRANS_SB_AGCOUNT            0x00000080
+#define        XFS_TRANS_SB_IMAXPCT            0x00000100
+#define        XFS_TRANS_SB_REXTSIZE           0x00000200
+#define        XFS_TRANS_SB_RBMBLOCKS          0x00000400
+#define        XFS_TRANS_SB_RBLOCKS            0x00000800
+#define        XFS_TRANS_SB_REXTENTS           0x00001000
+#define        XFS_TRANS_SB_REXTSLOG           0x00002000
+
+/*
+ * Here we centralize the specification of XFS meta-data buffer reference count
+ * values.  This determines how hard the buffer cache tries to hold onto the
+ * buffer.
+ */
+#define        XFS_AGF_REF             4
+#define        XFS_AGI_REF             4
+#define        XFS_AGFL_REF            3
+#define        XFS_INO_BTREE_REF       3
+#define        XFS_ALLOC_BTREE_REF     2
+#define        XFS_BMAP_BTREE_REF      2
+#define        XFS_DIR_BTREE_REF       2
+#define        XFS_INO_REF             2
+#define        XFS_ATTR_BTREE_REF      1
+#define        XFS_DQUOT_REF           1
+
+/*
+ * Flags for xfs_trans_ichgtime().
+ */
+#define        XFS_ICHGTIME_MOD        0x1     /* data fork modification timestamp */
+#define        XFS_ICHGTIME_CHG        0x2     /* inode field change timestamp */
+#define        XFS_ICHGTIME_CREATE     0x4     /* inode create timestamp */
+
+
+/*
+ * Symlink decoding/encoding functions
+ */
+int xfs_symlink_blocks(struct xfs_mount *mp, int pathlen);
+int xfs_symlink_hdr_set(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset,
+                       uint32_t size, struct xfs_buf *bp);
+bool xfs_symlink_hdr_ok(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset,
+                       uint32_t size, struct xfs_buf *bp);
+void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
+                                struct xfs_inode *ip, struct xfs_ifork *ifp);
+
+#endif /* __XFS_SHARED_H__ */
index 8968f5036fa17a5cda24c1bfdd9d7c3ed1957ca1..f317488263dd975eb85eb44d9746abfb974ef46f 100644 (file)
  */
 
 #include "xfs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_inum.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
-#include "xfs_ialloc.h"
 #include "xfs_bmap.h"
-#include "xfs_rtalloc.h"
+#include "xfs_alloc.h"
 #include "xfs_error.h"
-#include "xfs_itable.h"
 #include "xfs_fsops.h"
-#include "xfs_attr.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
+#include "xfs_log.h"
 #include "xfs_log_priv.h"
-#include "xfs_trans_priv.h"
-#include "xfs_filestream.h"
 #include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
 #include "xfs_dir2.h"
 #include "xfs_extfree_item.h"
 #include "xfs_mru_cache.h"
@@ -52,6 +44,9 @@
 #include "xfs_icache.h"
 #include "xfs_trace.h"
 #include "xfs_icreate_item.h"
+#include "xfs_dinode.h"
+#include "xfs_filestream.h"
+#include "xfs_quota.h"
 
 #include <linux/namei.h>
 #include <linux/init.h>
@@ -946,10 +941,6 @@ xfs_fs_destroy_inode(
 
        XFS_STATS_INC(vn_reclaim);
 
-       /* bad inode, get out here ASAP */
-       if (is_bad_inode(inode))
-               goto out_reclaim;
-
        ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
 
        /*
@@ -965,7 +956,6 @@ xfs_fs_destroy_inode(
         * this more efficiently than we can here, so simply let background
         * reclaim tear down all inodes.
         */
-out_reclaim:
        xfs_inode_set_reclaim_tag(ip);
 }
 
@@ -1165,7 +1155,7 @@ xfs_restore_resvblks(struct xfs_mount *mp)
  * Note: xfs_log_quiesce() stops background log work - the callers must ensure
  * it is started again when appropriate.
  */
-void
+static void
 xfs_quiesce_attr(
        struct xfs_mount        *mp)
 {
@@ -1246,7 +1236,7 @@ xfs_fs_remount(
                         */
 #if 0
                        xfs_info(mp,
-               "mount option \"%s\" not supported for remount\n", p);
+               "mount option \"%s\" not supported for remount", p);
                        return -EINVAL;
 #else
                        break;
@@ -1491,10 +1481,6 @@ xfs_fs_fill_super(
                error = ENOENT;
                goto out_unmount;
        }
-       if (is_bad_inode(root)) {
-               error = EINVAL;
-               goto out_unmount;
-       }
        sb->s_root = d_make_root(root);
        if (!sb->s_root) {
                error = ENOMEM;
index f622a97a7e3383d287d85a3787c2feecc8a36b3f..14e58f2c96bd71708f1c608536b835b25f05e795 100644 (file)
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
+#include "xfs_shared.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_dir2_format.h"
+#include "xfs_da_format.h"
 #include "xfs_dir2.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_ialloc.h"
 #include "xfs_alloc.h"
 #include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_bmap_util.h"
 #include "xfs_error.h"
 #include "xfs_quota.h"
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
 #include "xfs_symlink.h"
-#include "xfs_buf_item.h"
+#include "xfs_trans.h"
+#include "xfs_log.h"
+#include "xfs_dinode.h"
 
 /* ----- Kernel only functions below ----- */
 STATIC int
@@ -424,8 +424,7 @@ xfs_symlink(
  */
 STATIC int
 xfs_inactive_symlink_rmt(
-       xfs_inode_t     *ip,
-       xfs_trans_t     **tpp)
+       struct xfs_inode *ip)
 {
        xfs_buf_t       *bp;
        int             committed;
@@ -437,11 +436,9 @@ xfs_inactive_symlink_rmt(
        xfs_mount_t     *mp;
        xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS];
        int             nmaps;
-       xfs_trans_t     *ntp;
        int             size;
        xfs_trans_t     *tp;
 
-       tp = *tpp;
        mp = ip->i_mount;
        ASSERT(ip->i_df.if_flags & XFS_IFEXTENTS);
        /*
@@ -453,6 +450,16 @@ xfs_inactive_symlink_rmt(
         */
        ASSERT(ip->i_d.di_nextents > 0 && ip->i_d.di_nextents <= 2);
 
+       tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               return error;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
        /*
         * Lock the inode, fix the size, and join it to the transaction.
         * Hold it so in the normal path, we still have it locked for
@@ -471,7 +478,7 @@ xfs_inactive_symlink_rmt(
        error = xfs_bmapi_read(ip, 0, xfs_symlink_blocks(mp, size),
                                mval, &nmaps, 0);
        if (error)
-               goto error0;
+               goto error_trans_cancel;
        /*
         * Invalidate the block(s). No validation is done.
         */
@@ -481,22 +488,24 @@ xfs_inactive_symlink_rmt(
                        XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0);
                if (!bp) {
                        error = ENOMEM;
-                       goto error1;
+                       goto error_bmap_cancel;
                }
                xfs_trans_binval(tp, bp);
        }
        /*
         * Unmap the dead block(s) to the free_list.
         */
-       if ((error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps,
-                       &first_block, &free_list, &done)))
-               goto error1;
+       error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps,
+                           &first_block, &free_list, &done);
+       if (error)
+               goto error_bmap_cancel;
        ASSERT(done);
        /*
         * Commit the first transaction.  This logs the EFI and the inode.
         */
-       if ((error = xfs_bmap_finish(&tp, &free_list, &committed)))
-               goto error1;
+       error = xfs_bmap_finish(&tp, &free_list, &committed);
+       if (error)
+               goto error_bmap_cancel;
        /*
         * The transaction must have been committed, since there were
         * actually extents freed by xfs_bunmapi.  See xfs_bmap_finish.
@@ -510,27 +519,14 @@ xfs_inactive_symlink_rmt(
         */
        xfs_trans_ijoin(tp, ip, 0);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       /*
-        * Get a new, empty transaction to return to our caller.
-        */
-       ntp = xfs_trans_dup(tp);
        /*
         * Commit the transaction containing extent freeing and EFDs.
-        * If we get an error on the commit here or on the reserve below,
-        * we need to unlock the inode since the new transaction doesn't
-        * have the inode attached.
         */
-       error = xfs_trans_commit(tp, 0);
-       tp = ntp;
+       error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
        if (error) {
                ASSERT(XFS_FORCED_SHUTDOWN(mp));
-               goto error0;
+               goto error_unlock;
        }
-       /*
-        * transaction commit worked ok so we can drop the extra ticket
-        * reference that we gained in xfs_trans_dup()
-        */
-       xfs_log_ticket_put(tp->t_ticket);
 
        /*
         * Remove the memory for extent descriptions (just bookkeeping).
@@ -538,23 +534,16 @@ xfs_inactive_symlink_rmt(
        if (ip->i_df.if_bytes)
                xfs_idata_realloc(ip, -ip->i_df.if_bytes, XFS_DATA_FORK);
        ASSERT(ip->i_df.if_bytes == 0);
-       /*
-        * Put an itruncate log reservation in the new transaction
-        * for our caller.
-        */
-       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0);
-       if (error) {
-               ASSERT(XFS_FORCED_SHUTDOWN(mp));
-               goto error0;
-       }
 
-       xfs_trans_ijoin(tp, ip, 0);
-       *tpp = tp;
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
        return 0;
 
- error1:
+error_bmap_cancel:
        xfs_bmap_cancel(&free_list);
- error0:
+error_trans_cancel:
+       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+error_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
        return error;
 }
 
@@ -563,41 +552,46 @@ xfs_inactive_symlink_rmt(
  */
 int
 xfs_inactive_symlink(
-       struct xfs_inode        *ip,
-       struct xfs_trans        **tp)
+       struct xfs_inode        *ip)
 {
        struct xfs_mount        *mp = ip->i_mount;
        int                     pathlen;
 
        trace_xfs_inactive_symlink(ip);
 
-       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-
        if (XFS_FORCED_SHUTDOWN(mp))
                return XFS_ERROR(EIO);
 
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+
        /*
         * Zero length symlinks _can_ exist.
         */
        pathlen = (int)ip->i_d.di_size;
-       if (!pathlen)
+       if (!pathlen) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
                return 0;
+       }
 
        if (pathlen < 0 || pathlen > MAXPATHLEN) {
                xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)",
                         __func__, (unsigned long long)ip->i_ino, pathlen);
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
                ASSERT(0);
                return XFS_ERROR(EFSCORRUPTED);
        }
 
        if (ip->i_df.if_flags & XFS_IFINLINE) {
-               if (ip->i_df.if_bytes > 0)
+               if (ip->i_df.if_bytes > 0) 
                        xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
                                          XFS_DATA_FORK);
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
                ASSERT(ip->i_df.if_bytes == 0);
                return 0;
        }
 
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
        /* remove the remote symlink */
-       return xfs_inactive_symlink_rmt(ip, tp);
+       return xfs_inactive_symlink_rmt(ip);
 }
index 99338ba666ac68c11350fb1b24906364c7a6de01..e75245d0911611aeca2e78dfeae633c03bd34c3e 100644 (file)
@@ -22,6 +22,6 @@
 int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name,
                const char *target_path, umode_t mode, struct xfs_inode **ipp);
 int xfs_readlink(struct xfs_inode *ip, char *link);
-int xfs_inactive_symlink(struct xfs_inode *ip, struct xfs_trans **tpp);
+int xfs_inactive_symlink(struct xfs_inode *ip);
 
 #endif /* __XFS_SYMLINK_H */
index 01c85e3f64703e6b7a5c170c9e21421fe36d4e4e..bf59a2b45f8c40c431de3e8f52f3131d80d68a1c 100644 (file)
@@ -19,8 +19,9 @@
 #include "xfs.h"
 #include "xfs_fs.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
 #include "xfs_ag.h"
 #include "xfs_sb.h"
 #include "xfs_mount.h"
@@ -30,6 +31,7 @@
 #include "xfs_trace.h"
 #include "xfs_symlink.h"
 #include "xfs_cksum.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 
 
index 5d7b3e40705ffe4a96c75493d09ac74d9ae265cd..dee3279c095e6a32dcf460ca58acda0aa924bb2d 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
-#include "xfs_mount.h"
 #include "xfs_da_btree.h"
 #include "xfs_ialloc.h"
 #include "xfs_itable.h"
@@ -37,6 +34,8 @@
 #include "xfs_bmap.h"
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
+#include "xfs_trans.h"
+#include "xfs_log.h"
 #include "xfs_log_priv.h"
 #include "xfs_buf_item.h"
 #include "xfs_quota.h"
@@ -46,6 +45,7 @@
 #include "xfs_dquot.h"
 #include "xfs_log_recover.h"
 #include "xfs_inode_item.h"
+#include "xfs_bmap_btree.h"
 
 /*
  * We include this last to have the helpers above available for the trace
index 47910e638c187fdd46470ca2c4ca975f2d3c1334..425dfa45b9a087472676cf4f832138316d4b0fa4 100644 (file)
@@ -31,8 +31,8 @@ struct xfs_da_args;
 struct xfs_da_node_entry;
 struct xfs_dquot;
 struct xfs_log_item;
-struct xlog_ticket;
 struct xlog;
+struct xlog_ticket;
 struct xlog_recover;
 struct xlog_recover_item;
 struct xfs_buf_log_format;
@@ -135,6 +135,31 @@ DEFINE_PERAG_REF_EVENT(xfs_perag_clear_reclaim);
 DEFINE_PERAG_REF_EVENT(xfs_perag_set_eofblocks);
 DEFINE_PERAG_REF_EVENT(xfs_perag_clear_eofblocks);
 
+DECLARE_EVENT_CLASS(xfs_ag_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno),
+       TP_ARGS(mp, agno),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+       ),
+       TP_printk("dev %d:%d agno %u",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno)
+);
+#define DEFINE_AG_EVENT(name)  \
+DEFINE_EVENT(xfs_ag_class, name,       \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno),    \
+       TP_ARGS(mp, agno))
+
+DEFINE_AG_EVENT(xfs_read_agf);
+DEFINE_AG_EVENT(xfs_alloc_read_agf);
+DEFINE_AG_EVENT(xfs_read_agi);
+DEFINE_AG_EVENT(xfs_ialloc_read_agi);
+
 TRACE_EVENT(xfs_attr_list_node_descend,
        TP_PROTO(struct xfs_attr_list_context *ctx,
                 struct xfs_da_node_entry *btree),
@@ -938,6 +963,63 @@ DEFINE_LOG_ITEM_EVENT(xfs_ail_pinned);
 DEFINE_LOG_ITEM_EVENT(xfs_ail_locked);
 DEFINE_LOG_ITEM_EVENT(xfs_ail_flushing);
 
+DECLARE_EVENT_CLASS(xfs_ail_class,
+       TP_PROTO(struct xfs_log_item *lip, xfs_lsn_t old_lsn, xfs_lsn_t new_lsn),
+       TP_ARGS(lip, old_lsn, new_lsn),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(void *, lip)
+               __field(uint, type)
+               __field(uint, flags)
+               __field(xfs_lsn_t, old_lsn)
+               __field(xfs_lsn_t, new_lsn)
+       ),
+       TP_fast_assign(
+               __entry->dev = lip->li_mountp->m_super->s_dev;
+               __entry->lip = lip;
+               __entry->type = lip->li_type;
+               __entry->flags = lip->li_flags;
+               __entry->old_lsn = old_lsn;
+               __entry->new_lsn = new_lsn;
+       ),
+       TP_printk("dev %d:%d lip 0x%p old lsn %d/%d new lsn %d/%d type %s flags %s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->lip,
+                 CYCLE_LSN(__entry->old_lsn), BLOCK_LSN(__entry->old_lsn),
+                 CYCLE_LSN(__entry->new_lsn), BLOCK_LSN(__entry->new_lsn),
+                 __print_symbolic(__entry->type, XFS_LI_TYPE_DESC),
+                 __print_flags(__entry->flags, "|", XFS_LI_FLAGS))
+)
+
+#define DEFINE_AIL_EVENT(name) \
+DEFINE_EVENT(xfs_ail_class, name, \
+       TP_PROTO(struct xfs_log_item *lip, xfs_lsn_t old_lsn, xfs_lsn_t new_lsn), \
+       TP_ARGS(lip, old_lsn, new_lsn))
+DEFINE_AIL_EVENT(xfs_ail_insert);
+DEFINE_AIL_EVENT(xfs_ail_move);
+DEFINE_AIL_EVENT(xfs_ail_delete);
+
+TRACE_EVENT(xfs_log_assign_tail_lsn,
+       TP_PROTO(struct xlog *log, xfs_lsn_t new_lsn),
+       TP_ARGS(log, new_lsn),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_lsn_t, new_lsn)
+               __field(xfs_lsn_t, old_lsn)
+               __field(xfs_lsn_t, last_sync_lsn)
+       ),
+       TP_fast_assign(
+               __entry->dev = log->l_mp->m_super->s_dev;
+               __entry->new_lsn = new_lsn;
+               __entry->old_lsn = atomic64_read(&log->l_tail_lsn);
+               __entry->last_sync_lsn = atomic64_read(&log->l_last_sync_lsn);
+       ),
+       TP_printk("dev %d:%d new tail lsn %d/%d, old lsn %d/%d, last sync %d/%d",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 CYCLE_LSN(__entry->new_lsn), BLOCK_LSN(__entry->new_lsn),
+                 CYCLE_LSN(__entry->old_lsn), BLOCK_LSN(__entry->old_lsn),
+                 CYCLE_LSN(__entry->last_sync_lsn), BLOCK_LSN(__entry->last_sync_lsn))
+)
 
 DECLARE_EVENT_CLASS(xfs_file_class,
        TP_PROTO(struct xfs_inode *ip, size_t count, loff_t offset, int flags),
index 5411e01ab4527318b187846fa3e7a53ad6300eb3..c812c5c060de1caa7532f1cdcc63766a4a227207 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_error.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_btree.h"
-#include "xfs_ialloc.h"
-#include "xfs_alloc.h"
 #include "xfs_extent_busy.h"
-#include "xfs_bmap.h"
 #include "xfs_quota.h"
-#include "xfs_qm.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
-#include "xfs_trans_space.h"
-#include "xfs_inode_item.h"
-#include "xfs_log_priv.h"
-#include "xfs_buf_item.h"
+#include "xfs_log.h"
 #include "xfs_trace.h"
+#include "xfs_error.h"
 
 kmem_zone_t    *xfs_trans_zone;
 kmem_zone_t    *xfs_log_item_desc_zone;
index 09cf40b89e8c1d85817cc649b22a12c3ba97aa78..9b96d35e483de4cb075170aef47db2a1f06a307e 100644 (file)
 #ifndef        __XFS_TRANS_H__
 #define        __XFS_TRANS_H__
 
-struct xfs_log_item;
-
-#include "xfs_trans_resv.h"
-
 /* kernel only transaction subsystem defines */
 
 struct xfs_buf;
@@ -77,6 +73,9 @@ struct xfs_item_ops {
        void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
 };
 
+void   xfs_log_item_init(struct xfs_mount *mp, struct xfs_log_item *item,
+                         int type, const struct xfs_item_ops *ops);
+
 /*
  * Return values for the iop_push() routines.
  */
@@ -85,18 +84,12 @@ struct xfs_item_ops {
 #define XFS_ITEM_LOCKED                2
 #define XFS_ITEM_FLUSHING      3
 
-/*
- * This is the type of function which can be given to xfs_trans_callback()
- * to be called upon the transaction's commit to disk.
- */
-typedef void (*xfs_trans_callback_t)(struct xfs_trans *, void *);
 
 /*
  * This is the structure maintained for every active transaction.
  */
 typedef struct xfs_trans {
        unsigned int            t_magic;        /* magic number */
-       xfs_log_callback_t      t_logcb;        /* log callback struct */
        unsigned int            t_type;         /* transaction type */
        unsigned int            t_log_res;      /* amt of log space resvd */
        unsigned int            t_log_count;    /* count for perm log res */
@@ -132,7 +125,6 @@ typedef struct xfs_trans {
        int64_t                 t_rextents_delta;/* superblocks rextents chg */
        int64_t                 t_rextslog_delta;/* superblocks rextslog chg */
        struct list_head        t_items;        /* log item descriptors */
-       xfs_trans_header_t      t_header;       /* header for in-log trans */
        struct list_head        t_busy;         /* list of busy extents */
        unsigned long           t_pflags;       /* saved process flags state */
 } xfs_trans_t;
@@ -237,10 +229,16 @@ void              xfs_trans_log_efd_extent(xfs_trans_t *,
                                         xfs_fsblock_t,
                                         xfs_extlen_t);
 int            xfs_trans_commit(xfs_trans_t *, uint flags);
+int            xfs_trans_roll(struct xfs_trans **, struct xfs_inode *);
 void           xfs_trans_cancel(xfs_trans_t *, int);
 int            xfs_trans_ail_init(struct xfs_mount *);
 void           xfs_trans_ail_destroy(struct xfs_mount *);
 
+void           xfs_trans_buf_set_type(struct xfs_trans *, struct xfs_buf *,
+                                      enum xfs_blft);
+void           xfs_trans_buf_copy_type(struct xfs_buf *dst_bp,
+                                       struct xfs_buf *src_bp);
+
 extern kmem_zone_t     *xfs_trans_zone;
 extern kmem_zone_t     *xfs_log_item_desc_zone;
 
index 21c6d7ddbc06b474e102b30cb6a13c7690d1a2a5..a7287354e53534b06e1816b189633d063f9e1548 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
 #include "xfs_trace.h"
 #include "xfs_error.h"
+#include "xfs_log.h"
 
 #ifdef DEBUG
 /*
@@ -658,11 +659,13 @@ xfs_trans_ail_update_bulk(
                        if (XFS_LSN_CMP(lsn, lip->li_lsn) <= 0)
                                continue;
 
+                       trace_xfs_ail_move(lip, lip->li_lsn, lsn);
                        xfs_ail_delete(ailp, lip);
                        if (mlip == lip)
                                mlip_changed = 1;
                } else {
                        lip->li_flags |= XFS_LI_IN_AIL;
+                       trace_xfs_ail_insert(lip, 0, lsn);
                }
                lip->li_lsn = lsn;
                list_add(&lip->li_ail, &tmp);
@@ -731,6 +734,7 @@ xfs_trans_ail_delete_bulk(
                        return;
                }
 
+               trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
                xfs_ail_delete(ailp, lip);
                lip->li_flags &= ~XFS_LI_IN_AIL;
                lip->li_lsn = 0;
index 8c75b8f672702419beede8e54363ec259616039a..c035d11b7734196c4fd689121d945e598a8d6d7a 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_trans_priv.h"
 #include "xfs_error.h"
index 54ee3c5dee76093b6a6136a5fde759a8be309ccd..cd2a10e15d3ac5520661ed2877f2e5c9cdac48d9 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
-#include "xfs_itable.h"
-#include "xfs_bmap.h"
-#include "xfs_rtalloc.h"
 #include "xfs_error.h"
-#include "xfs_attr.h"
-#include "xfs_buf_item.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
+#include "xfs_quota.h"
 #include "xfs_qm.h"
 
 STATIC void    xfs_trans_alloc_dqinfo(xfs_trans_t *);
index 8d71b16eccaeaca46a0a7e92a7bb69ac3d1a4b4e..47978ba89dae35307c5341d7b50bc3dc3728d140 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
 #include "xfs_extfree_item.h"
 
index 53dfe46f3680791a8eab2e72c1a92db3cb17c091..50c3f5614288febe4c85fdb7c231887527e80f79 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
-#include "xfs_btree.h"
+#include "xfs_trans.h"
 #include "xfs_trans_priv.h"
 #include "xfs_inode_item.h"
 #include "xfs_trace.h"
@@ -114,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 c52def0b441cd89f2cb207e1b7ba5a9f22cc915c..12e86af9d9b94327ea13384fe4a6c2018bf84e1d 100644 (file)
@@ -27,7 +27,6 @@ struct xfs_log_vec;
 
 
 void   xfs_trans_init(struct xfs_mount *);
-int    xfs_trans_roll(struct xfs_trans **, struct xfs_inode *);
 void   xfs_trans_add_item(struct xfs_trans *, struct xfs_log_item *);
 void   xfs_trans_del_item(struct xfs_log_item *);
 void   xfs_trans_free_items(struct xfs_trans *tp, xfs_lsn_t commit_lsn,
index a65a3cc40610abe92b0b158a83329190d6cbdf91..2fd59c0dae667b58029bad83b56dfad4958e4e0c 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
 #include "xfs_format.h"
-#include "xfs_log.h"
+#include "xfs_log_format.h"
 #include "xfs_trans_resv.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
 #include "xfs_mount.h"
-#include "xfs_error.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
-#include "xfs_btree.h"
+#include "xfs_bmap_btree.h"
 #include "xfs_ialloc.h"
-#include "xfs_alloc.h"
-#include "xfs_extent_busy.h"
-#include "xfs_bmap.h"
-#include "xfs_bmap_util.h"
 #include "xfs_quota.h"
+#include "xfs_trans.h"
 #include "xfs_qm.h"
 #include "xfs_trans_space.h"
 #include "xfs_trace.h"
@@ -393,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 db14d0c08682b90949f031532069f715ca628804..3e8e797c6d110ce01b0964a2e3845d8a59defd0b 100644 (file)
@@ -24,14 +24,6 @@ struct file;
 struct xfs_inode;
 struct attrlist_cursor_kern;
 
-/*
- * Return values for xfs_inactive.  A return value of
- * VN_INACTIVE_NOCACHE implies that the file system behavior
- * has disassociated its state and bhv_desc_t from the vnode.
- */
-#define        VN_INACTIVE_CACHE       0
-#define        VN_INACTIVE_NOCACHE     1
-
 /*
  * Flags for read/write calls - same values as IRIX
  */
index e01f35ea76ba436310f11d82b9fdf78322d8f6fe..9d479073ba415d6b482fbecbf160b6b99e4e859c 100644 (file)
  */
 
 #include "xfs.h"
+#include "xfs_format.h"
 #include "xfs_log_format.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_mount.h"
+#include "xfs_da_format.h"
 #include "xfs_inode.h"
 #include "xfs_attr.h"
 #include "xfs_attr_leaf.h"
index d98c67001840b705db746e4a1bc89ccba209652b..3ea214cff349c87482d4d3a29b0370bb6c90cd42 100644 (file)
@@ -83,7 +83,9 @@
  * Should the subsystem abort the loading of an ACPI table if the
  * table checksum is incorrect?
  */
+#ifndef ACPI_CHECKSUM_ABORT
 #define ACPI_CHECKSUM_ABORT             FALSE
+#endif
 
 /*
  * Generate a version of ACPICA that only supports "reduced hardware"
index 89c60b0f640819c7ed93b40774d5b9c3f3cbadfb..c602c7718421ded2f2bbe50f1e76edc39ac2b6f2 100644 (file)
@@ -100,6 +100,7 @@ enum acpi_hotplug_mode {
 struct acpi_hotplug_profile {
        struct kobject kobj;
        bool enabled:1;
+       bool ignore:1;
        enum acpi_hotplug_mode mode;
 };
 
@@ -431,9 +432,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 d8f9457755b4168787a66f903c15cb9d6506af25..4278aba9650381c932a687ca871a47f874b6aa1e 100644 (file)
@@ -46,7 +46,7 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20130927
+#define ACPI_CA_VERSION                 0x20131115
 
 #include <acpi/acconfig.h>
 #include <acpi/actypes.h>
index aea9e45efce63572eee4daafc2ecfffcdd104b6e..14909b0b9cae71d879df88963a688d37a0e98a8a 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)                                      \
diff --git a/include/asm-generic/simd.h b/include/asm-generic/simd.h
new file mode 100644 (file)
index 0000000..f57eb7b
--- /dev/null
@@ -0,0 +1,14 @@
+
+#include <linux/hardirq.h>
+
+/*
+ * may_use_simd - whether it is allowable at this time to issue SIMD
+ *                instructions or access the SIMD register file
+ *
+ * As architectures typically don't preserve the SIMD register file when
+ * taking an interrupt, !in_interrupt() should be a reasonable default.
+ */
+static __must_check inline bool may_use_simd(void)
+{
+       return !in_interrupt();
+}
index 83e2c31e8b007528f67f726ec47bc648f24bdda8..bc2121fa9132cc9cad2209cacee3896fdbeb5370 100644 (file)
 #define KERNEL_CTORS() . = ALIGN(8);                      \
                        VMLINUX_SYMBOL(__ctors_start) = .; \
                        *(.ctors)                          \
+                       *(.init_array)                     \
                        VMLINUX_SYMBOL(__ctors_end) = .;
 #else
 #define KERNEL_CTORS()
index 418d270e18063517750f39c68490f56fb7cd24c3..e73c19e90e38f49e9ebdff10bfed3cdca90a9f9a 100644 (file)
@@ -386,5 +386,21 @@ static inline int crypto_requires_sync(u32 type, u32 mask)
        return (type ^ CRYPTO_ALG_ASYNC) & mask & CRYPTO_ALG_ASYNC;
 }
 
-#endif /* _CRYPTO_ALGAPI_H */
+noinline unsigned long __crypto_memneq(const void *a, const void *b, size_t size);
+
+/**
+ * crypto_memneq - Compare two areas of memory without leaking
+ *                timing information.
+ *
+ * @a: One area of memory
+ * @b: Another area of memory
+ * @size: The size of the area.
+ *
+ * Returns 0 when data is equal, 1 otherwise.
+ */
+static inline int crypto_memneq(const void *a, const void *b, size_t size)
+{
+       return __crypto_memneq(a, b, size) != 0UL ? 1 : 0;
+}
 
+#endif /* _CRYPTO_ALGAPI_H */
index e47b044929a84b7cd1e54fb17b8e87de3020d7b6..6775059539b56f2ffe870d28c4d2ab35821c013a 100644 (file)
@@ -23,5 +23,15 @@ struct crypto_authenc_key_param {
        __be32 enckeylen;
 };
 
-#endif /* _CRYPTO_AUTHENC_H */
+struct crypto_authenc_keys {
+       const u8 *authkey;
+       const u8 *enckey;
+
+       unsigned int authkeylen;
+       unsigned int enckeylen;
+};
 
+int crypto_authenc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
+                              unsigned int keylen);
+
+#endif /* _CRYPTO_AUTHENC_H */
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 f5b0224c99679ed23b8475a75b78992d3ca8c899..fc09732613adbe98985083a0ca8fc0ec5ea334a9 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 b46fb45f2cca4a5881b64d93577e6ae03a221670..1d4a920ef7ff4ffea56470ef24c2e6cf8a7b0696 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 24f499569a2f70480d87dca321c222c50755190d..f32c5cd51f4125a455a81ff60cd190c1383d31e1 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 f43d556bf40bcbe1968d2440e214160c02844a59..ef6ad3a8e58e517f31624767fb1751f7ada463c5 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 ae8dbfb1207c71a6cbe44dc166180988083c00e6..a92c3754e3bbffe56c286d85bd2212f753ebe2c3 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 3d79e513c0b3663ef9cd08850801a2d038ee721d..87578c109e4869bbecf5cce3a7530a0c2fb80637 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 3abfa6ea226edda57aed2b9efaf970dcae1d3c35..97d5497debc1c7dc0b7b6c75199810bf13b6c03f 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 8a10f5c354e6cafb857beca40f9e911a86b7269d..940ece4934bab0e0c17f491b28da059454967621 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 751eaffbf0d5fe5ef758254ceba1fe706af4423d..ee127ec33c608b06899ee9d69807f9918a78f2c5 100644 (file)
@@ -169,6 +169,7 @@ struct ttm_tt;
  * @offset: The current GPU offset, which can have different meanings
  * depending on the memory type. For SYSTEM type memory, it should be 0.
  * @cur_placement: Hint of current placement.
+ * @wu_mutex: Wait unreserved mutex.
  *
  * Base class for TTM buffer object, that deals with data placement and CPU
  * mappings. GPU mappings are really up to the driver, but for simpler GPUs
@@ -250,6 +251,7 @@ struct ttm_buffer_object {
 
        struct reservation_object *resv;
        struct reservation_object ttm_resv;
+       struct mutex wu_mutex;
 };
 
 /**
@@ -702,5 +704,5 @@ extern ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
                         size_t count, loff_t *f_pos, bool write);
 
 extern void ttm_bo_swapout_all(struct ttm_bo_device *bdev);
-
+extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo);
 #endif
index ec8a1d306510e1f5c92fc7193a777e6f4b73caf2..16db7d01a33668b576d9888fa8335bc7cc3346c9 100644 (file)
@@ -70,7 +70,8 @@ extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
 /**
  * function ttm_eu_reserve_buffers
  *
- * @ticket:  [out] ww_acquire_ctx returned by call.
+ * @ticket:  [out] ww_acquire_ctx filled in by call, or NULL if only
+ *           non-blocking reserves should be tried.
  * @list:    thread private list of ttm_validate_buffer structs.
  *
  * Tries to reserve bos pointed to by the list entries for validation.
index fc0cf064990196534a7e93097860277f8b21e7e6..58b029894eb33ea32dff2ba2977546c097f2b533 100644 (file)
@@ -41,6 +41,7 @@
 #include <drm/drm_hashtab.h>
 #include <linux/kref.h>
 #include <linux/rcupdate.h>
+#include <linux/dma-buf.h>
 #include <ttm/ttm_memory.h>
 
 /**
@@ -77,6 +78,7 @@ enum ttm_object_type {
        ttm_fence_type,
        ttm_buffer_type,
        ttm_lock_type,
+       ttm_prime_type,
        ttm_driver_type0 = 256,
        ttm_driver_type1,
        ttm_driver_type2,
@@ -132,6 +134,30 @@ struct ttm_base_object {
                                 enum ttm_ref_type ref_type);
 };
 
+
+/**
+ * struct ttm_prime_object - Modified base object that is prime-aware
+ *
+ * @base: struct ttm_base_object that we derive from
+ * @mutex: Mutex protecting the @dma_buf member.
+ * @size: Size of the dma_buf associated with this object
+ * @real_type: Type of the underlying object. Needed since we're setting
+ * the value of @base::object_type to ttm_prime_type
+ * @dma_buf: Non ref-coutned pointer to a struct dma_buf created from this
+ * object.
+ * @refcount_release: The underlying object's release method. Needed since
+ * we set @base::refcount_release to our own release method.
+ */
+
+struct ttm_prime_object {
+       struct ttm_base_object base;
+       struct mutex mutex;
+       size_t size;
+       enum ttm_object_type real_type;
+       struct dma_buf *dma_buf;
+       void (*refcount_release) (struct ttm_base_object **);
+};
+
 /**
  * ttm_base_object_init
  *
@@ -248,14 +274,18 @@ extern void ttm_object_file_release(struct ttm_object_file **p_tfile);
 /**
  * ttm_object device init - initialize a struct ttm_object_device
  *
+ * @mem_glob: struct ttm_mem_global for memory accounting.
  * @hash_order: Order of hash table used to hash the base objects.
+ * @ops: DMA buf ops for prime objects of this device.
  *
  * This function is typically called on device initialization to prepare
  * data structures needed for ttm base and ref objects.
  */
 
-extern struct ttm_object_device *ttm_object_device_init
-    (struct ttm_mem_global *mem_glob, unsigned int hash_order);
+extern struct ttm_object_device *
+ttm_object_device_init(struct ttm_mem_global *mem_glob,
+                      unsigned int hash_order,
+                      const struct dma_buf_ops *ops);
 
 /**
  * ttm_object_device_release - release data held by a ttm_object_device
@@ -272,4 +302,31 @@ extern void ttm_object_device_release(struct ttm_object_device **p_tdev);
 
 #define ttm_base_object_kfree(__object, __base)\
        kfree_rcu(__object, __base.rhead)
+
+extern int ttm_prime_object_init(struct ttm_object_file *tfile,
+                                size_t size,
+                                struct ttm_prime_object *prime,
+                                bool shareable,
+                                enum ttm_object_type type,
+                                void (*refcount_release)
+                                (struct ttm_base_object **),
+                                void (*ref_obj_release)
+                                (struct ttm_base_object *,
+                                 enum ttm_ref_type ref_type));
+
+static inline enum ttm_object_type
+ttm_base_object_type(struct ttm_base_object *base)
+{
+       return (base->object_type == ttm_prime_type) ?
+               container_of(base, struct ttm_prime_object, base)->real_type :
+               base->object_type;
+}
+extern int ttm_prime_fd_to_handle(struct ttm_object_file *tfile,
+                                 int fd, u32 *handle);
+extern int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
+                                 uint32_t handle, uint32_t flags,
+                                 int *prime_fd);
+
+#define ttm_prime_object_kfree(__obj, __prime)         \
+       kfree_rcu(__obj, __prime.base.rhead)
 #endif
index 706b962c6467e7145f6b1a1c42700b406bfb203c..d1f61bfe0ebe49841cc5f249c8307b75973b98a7 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 cf49159b0e3a4f47c7890a122ae44ea0dc35a5da..fca5c62340a47fbbb25386002d7c8293fab7a40b 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 b0972c4ce81c3cc7e8ad1bbb03c5dc9054f2f32a..d9099b15b4726404343308a39e5e55d39a16aa05 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 62d9303c283789baacfdc3e745d11bb2110431fe..0ddb5c02ad8b6c279047c4c8c9c90e5516327ca7 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 729a4d165bcc52b444ee0ea89e8faac36f6518f1..a40641954c296c3042c0e34f5c1ff170aee5cfa5 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 f26ec20f635476a88c8879baad16a5a908433fa7..1b135d49b27985d3243cdbf92d6002ce5eea8597 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 98e892ef6d5a53bcded79a65c966f469abd083f6..a0f9280421eca511b00e80f51e8e65436ec47e3a 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 22c33e35bcb20c76d5107ef221c7869bc8aaf410..5d5aaae3af433ff62b6e03f107c7dfe88a06b522 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 5bd6ab9b0c2750fca49409c9714d41f1ce7a2d9f..dc196bbcf227288bce4d4d3e2db60dae5cde3dec 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 7a7cc74d7f27e9901e59d2a61b850c0ec6acbe9e..d48dc00232a436cbf67b577b54a8bd27336a161c 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 b025925df7f75a5b86fa894de93e2e83d0bdf571..952b01033c32dedcf83349a988b920c1c5ed8aa8 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 0bc727534108d5a2d5d527e75eaa8020a3ccd239..41cf0c399288e022edf32f7e65c6f151004829d9 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 412cd509effe0ca3b73e5524e121921457296681..3f2793d51899c2e56c0a948918c4d7e3023b6cb2 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 bf5d574ebdf4c94da0238c14add54bc51587175a..121f11f001c06f1cdcdeb6533234d76626a2915b 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 9f15c0064c50586ed08ee740698bcc6f2450c959..31ea4b428360c7e22929325ce90a76aee00a046b 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 5eaa746735ff7f4ce73f0ac5f0ed11ba78a24d50..8c9b7a1c4138b87a79833bc400370a13e94cf270 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,12 +241,16 @@ 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 */
 
 #ifdef CONFIG_PERF_EVENTS
        int                             perf_refcount;
        struct hlist_head __percpu      *perf_events;
+
+       int     (*perf_perm)(struct ftrace_event_call *,
+                            struct perf_event *);
 #endif
 };
 
@@ -253,6 +260,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 +270,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 +279,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 +288,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;
@@ -306,16 +320,33 @@ struct ftrace_event_file {
        }                                                               \
        early_initcall(trace_init_flags_##name);
 
+#define __TRACE_EVENT_PERF_PERM(name, expr...)                         \
+       static int perf_perm_##name(struct ftrace_event_call *tp_event, \
+                                   struct perf_event *p_event)         \
+       {                                                               \
+               return ({ expr; });                                     \
+       }                                                               \
+       static int __init trace_init_perf_perm_##name(void)             \
+       {                                                               \
+               event_##name.perf_perm = &perf_perm_##name;             \
+               return 0;                                               \
+       }                                                               \
+       early_initcall(trace_init_perf_perm_##name);
+
 #define PERF_MAX_TRACE_SIZE    2048
 
 #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 023bc346b877f08bd75be65edaba526af7314558..c0894dd8827b27378d31bb88f591bc2fd1e43a9c 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)
index 656a27efb2c8cdd755cd78e20a36e5dd028e5310..82eac610ce1a77ba944ee99d4afe6c921690b3c5 100644 (file)
@@ -125,6 +125,13 @@ extern struct gpio_chip *gpiochip_find(void *data,
 int gpiod_lock_as_irq(struct gpio_desc *desc);
 void gpiod_unlock_as_irq(struct gpio_desc *desc);
 
+enum gpio_lookup_flags {
+       GPIO_ACTIVE_HIGH = (0 << 0),
+       GPIO_ACTIVE_LOW = (1 << 0),
+       GPIO_OPEN_DRAIN = (1 << 1),
+       GPIO_OPEN_SOURCE = (1 << 2),
+};
+
 /**
  * Lookup table for associating GPIOs to specific devices and functions using
  * platform data.
@@ -152,9 +159,9 @@ struct gpiod_lookup {
         */
        unsigned int idx;
        /*
-        * mask of GPIOF_* values
+        * mask of GPIO_* values
         */
-       unsigned long flags;
+       enum gpio_lookup_flags flags;
 };
 
 /*
index a265af294ea49a28c0384af71aa5ee1a249b3be8..206a2af6b62b176fbbb16e2cca923053edc5877a 100644 (file)
@@ -21,6 +21,8 @@
 
 #include <linux/hid.h>
 #include <linux/hid-sensor-ids.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
 
 /**
  * struct hid_sensor_hub_attribute_info - Attribute info
@@ -184,6 +186,7 @@ struct hid_sensor_common {
        struct platform_device *pdev;
        unsigned usage_id;
        bool data_ready;
+       struct iio_trigger *trigger;
        struct hid_sensor_hub_attribute_info poll;
        struct hid_sensor_hub_attribute_info report_state;
        struct hid_sensor_hub_attribute_info power_state;
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 3935428c57cff80d01236428fdea979d7a9f911b..91672e2deec36cd4c986116c32b527fe5f934e19 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 0393270466c3fd8a59d24781cf54ab5ffd0c5a57..9649ff0c63f8d0bb5253cd08b3f32499986b4cfd 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 2ab11dc38077c89f7c60d767ed1096b909ab6039..eff50e062be850189ed9e78a9b6058a5eea49b74 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 c2702856295ebaaa06db6dddb7106a53d2e71315..84ba5ac39e039f80da563305155800cd1332782c 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 c9e831dc80bccb10efaec812787f75b57fbf5b71..db43b58a3355c7b97d3acb0902fede4a0883790b 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 7ea319e95b4771ecb9dc4be7d04ad24a8e628d86..a444c790fa7235e2bc7247313804a5062d0a4db8 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 56bb0dc8b7d44cbab1a5a3cefb9efc04a7440995..7dc10036eff552229cdd964e88669a7759a8152f 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 714ba08dc09265922fe8ab81dee1caadc12a9100..e374e369fb2f4c9eb5ace48679376550d5ee633c 100644 (file)
@@ -14,6 +14,6 @@ enum irqreturn {
 };
 
 typedef enum irqreturn irqreturn_t;
-#define IRQ_RETVAL(x)  ((x) != IRQ_NONE)
+#define IRQ_RETVAL(x)  ((x) ? IRQ_HANDLED : IRQ_NONE)
 
 #endif
index 672ddc4de4af0511b20a4d269bcae1c517c3d5c4..2ac02772a86e3c1db0be81c7fb68d331aba02977 100644 (file)
@@ -393,6 +393,15 @@ extern int panic_on_oops;
 extern int panic_on_unrecovered_nmi;
 extern int panic_on_io_nmi;
 extern int sysctl_panic_on_stackoverflow;
+/*
+ * Only to be used by arch init code. If the user over-wrote the default
+ * CONFIG_PANIC_TIMEOUT, honor it.
+ */
+static inline void set_arch_panic_timeout(int timeout, int arch_default_timeout)
+{
+       if (panic_timeout == arch_default_timeout)
+               panic_timeout = timeout;
+}
 extern const char *print_tainted(void);
 enum lockdep_ok {
        LOCKDEP_STILL_OK,
@@ -501,7 +510,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 +647,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 518a53afb9ea24b301d87d4af6c824bca5fc5548..a74c3a84dfdd05223cc0613c9445bdc396e1afe9 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 4dfde1161c5e7878565d05ad7f5293e9e4d19cef..80d677483e31f95ce4b08ef00ffb79c239932c18 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 10308c6a3d1c47e67c9b89a4900fff696210b67d..552d51efb429c0867337d845266744c8aac9fb1f 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 0fbbc7aa02cb17c9c7d1cc5b5a17330d58858c10..9523d2ad7535c42be6881ee882b95f8ce1efad9a 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 8828a78dec9a2b3ed30d4c91f872405a8594f51a..fbf10a0bc0957c8bb2609da00969d88429797794 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 13dfd36a329474df228102dd49bafbdf1b5c687d..c8929c3832db26d00d80f96e524afbb0e75c4df1 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 4706d3d46e56f1e4bd630bc0dcbb50e8111d4af8..cb49417f8ba9261ba436fef11851274f9df977d0 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 7314fc4e6d2503b0e5e885c7ceccf0a873207ec0..bdba8c61207bb7b892e63eff49d81cfdf2f3538a 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 786d02eb79d2210ef8c3dfe145e3c8d0e0123ef1..21e21b81cc7551db7203b7d027b688feebd3577c 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 244fb0d51589ffc9d92ed440bed1cb8f56084ae0..3e050b933dd0e28117a772e68e093a90ca6feddb 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 676f0f388992410fe0b7ac918a08bfe992d1ca91..3f3dc45f93ee8058bab7048c3476fa508a9bb27f 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 d1382dfbeff022b5f91dfcf6789ece80874eea5b..0ce7721055081b3a75e16ea9b485c01b7e83a1f8 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 ba89b94e4a567ee5499ccd8964eb3526928654bf..674b45d5a757bc66fff49abb26aebed88929c9b0 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 b473577f36db5fad660396618ed5fd870e008407..8789fa3c7fd9d75dbc7fef3a0217ed3700112a35 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 08cce7f96ab9365359c30a4726d910022f571be5..d498d98f0c2cbfd3c9076aa439433a23caa80899 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 40854ac0ba3d0cc6fdfd3116275db97d15ec3811..eefafa62d3044d8f1a828a8a5f44d454251193ba 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 5eb4e31af22b8e05356dfef793c1b96497af8cb3..da78875807fc33bb7c77dff5d374e474ba815f31 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 6b8c496572c841d8ec8be70740557f1caf50b79a..554548cd3dd4683145ce5ef0a4b1e66fb829365d 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 42a35d94b82caaf6c74e86befb923650440970ea..1cedd000cf293f4486575b87086b01e2ee1b26e5 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 a3198e5aaf4e4dac55a70caae5669e8ab84906e3..bd299418a934e21b99c303af82a7c2f427bbf915 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,8 +154,12 @@ 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 842de3e21e70c4125f3d1574ebb0391a8d2b4cd5..176fdf824b14733bf5a37ca48a3f7993f7544048 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 da51bec578c33043c156d6661668e8957f5cb86f..87079fc38011ccaca33f4ef6ad7d5d055ed3087e 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 198f0fa44e9fd5c8f2b351c38f7679f165f89f7b..6ce7d2cd3c7ad7dd5eea530192de207b3d1aef22 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 3b0c33ae13e1d7b32b051a2cb9a673d641914876..99f5709ac343df3d6c5b7bbda636eeeb42c4e14c 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 05f2447f8c15ba5c13b2ec329baf6feaea0b031e..15cd6b1b211e731c5c133dabd0d26e890a56e135 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 87cce50bd121aa13803e820a23ea34584ccb1a39..009b02481436ff34ab9d3cfd93ea75070720b354 100644 (file)
@@ -26,11 +26,11 @@ struct msi_desc {
        struct {
                __u8    is_msix : 1;
                __u8    multiple: 3;    /* log2 number of messages */
-               __u8    maskbit : 1;    /* mask-pending bit supported ?   */
-               __u8    is_64   : 1;    /* Address size: 0=32bit 1=64bit  */
-               __u8    pos;            /* Location of the msi capability */
-               __u16   entry_nr;       /* specific enabled entry         */
-               unsigned default_irq;   /* default pre-assigned irq       */
+               __u8    maskbit : 1;    /* mask-pending bit supported ? */
+               __u8    is_64   : 1;    /* Address size: 0=32bit 1=64bit */
+               __u8    pos;            /* Location of the msi capability */
+               __u16   entry_nr;       /* specific enabled entry */
+               unsigned default_irq;   /* default pre-assigned irq */
        } msi_attrib;
 
        u32 masked;                     /* mask bits */
index b292a04355717a503b093e2d2c424cf92951415f..4bcee94cef9314c44dfaa94fb17d3c905fefee08 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 8b3de7cc2ffc24f12ade53f8680d2851d7bf031b..7f0ed423a3606f1cc13e09a00d332dc4a45f924b 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 c6f41b616965b7b872879d873e49473e0fe81a6e..c1637062c1ce138129a87dd571a5ea7cd2b53abd 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 86292beebfe2b910e28491dfd0e9c2343428000d..438694650471cc66b62cb7890e19fde5799314f7 100644 (file)
@@ -129,10 +129,9 @@ struct parallel_data {
        struct padata_serial_queue      __percpu *squeue;
        atomic_t                        reorder_objects;
        atomic_t                        refcnt;
+       atomic_t                        seq_nr;
        struct padata_cpumask           cpumask;
        spinlock_t                      lock ____cacheline_aligned;
-       spinlock_t                      seq_lock;
-       unsigned int                    seq_nr;
        unsigned int                    processed;
        struct timer_list               timer;
 };
index d006f0ca60f46e92705fb8b3fd315ebacc5242b2..5a462c4e5009d68960525d2167728d59ee75e33b 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 835ec7bf6c05a0d40a5144c4abe6b10012333087..1084a15175e04ff0bc715e3e429aab7027d279f8 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/irqreturn.h>
 #include <uapi/linux/pci.h>
 
-/* Include the ID list */
 #include <linux/pci_ids.h>
 
 /*
  *
  *     7:3 = slot
  *     2:0 = function
- * PCI_DEVFN(), PCI_SLOT(), and PCI_FUNC() are defined uapi/linux/pci.h
+ *
+ * PCI_DEVFN(), PCI_SLOT(), and PCI_FUNC() are defined in uapi/linux/pci.h.
  * In the interest of not exposing interfaces to user-space unnecessarily,
- * the following kernel only defines are being added here.
+ * the following kernel-only defines are being added here.
  */
 #define PCI_DEVID(bus, devfn)  ((((u16)bus) << 8) | devfn)
 /* return bus from PCI devid = ((u16)bus_number) << 8) | devfn */
@@ -153,10 +153,10 @@ enum pcie_reset_state {
        /* Reset is NOT asserted (Use to deassert reset) */
        pcie_deassert_reset = (__force pcie_reset_state_t) 1,
 
-       /* Use #PERST to reset PCI-E device */
+       /* Use #PERST to reset PCIe device */
        pcie_warm_reset = (__force pcie_reset_state_t) 2,
 
-       /* Use PCI-E Hot Reset to reset device */
+       /* Use PCIe Hot Reset to reset device */
        pcie_hot_reset = (__force pcie_reset_state_t) 3
 };
 
@@ -259,13 +259,13 @@ struct pci_dev {
        unsigned int    class;          /* 3 bytes: (base,sub,prog-if) */
        u8              revision;       /* PCI revision, low byte of class word */
        u8              hdr_type;       /* PCI header type (`multi' flag masked out) */
-       u8              pcie_cap;       /* PCI-E capability offset */
+       u8              pcie_cap;       /* PCIe capability offset */
        u8              msi_cap;        /* MSI capability offset */
        u8              msix_cap;       /* MSI-X capability offset */
-       u8              pcie_mpss:3;    /* PCI-E Max Payload Size Supported */
+       u8              pcie_mpss:3;    /* PCIe Max Payload Size Supported */
        u8              rom_base_reg;   /* which config register controls the ROM */
-       u8              pin;            /* which interrupt pin this device uses */
-       u16             pcie_flags_reg; /* cached PCI-E Capabilities Register */
+       u8              pin;            /* which interrupt pin this device uses */
+       u16             pcie_flags_reg; /* cached PCIe Capabilities Register */
 
        struct pci_driver *driver;      /* which driver has allocated this device */
        u64             dma_mask;       /* Mask of the bits of bus address this
@@ -300,7 +300,7 @@ struct pci_dev {
        unsigned int    d3cold_delay;   /* D3cold->D0 transition time in ms */
 
 #ifdef CONFIG_PCIEASPM
-       struct pcie_link_state  *link_state;    /* ASPM link state. */
+       struct pcie_link_state  *link_state;    /* ASPM link state */
 #endif
 
        pci_channel_state_t error_state;        /* current connectivity state */
@@ -317,7 +317,7 @@ struct pci_dev {
 
        bool match_driver;              /* Skip attaching driver */
        /* These fields are used by common fixups */
-       unsigned int    transparent:1;  /* Transparent PCI bridge */
+       unsigned int    transparent:1;  /* Subtractive decode PCI bridge */
        unsigned int    multifunction:1;/* Part of multi-function device */
        /* keep track of device state */
        unsigned int    is_added:1;
@@ -326,7 +326,7 @@ struct pci_dev {
        unsigned int    block_cfg_access:1;     /* config space access is blocked */
        unsigned int    broken_parity_status:1; /* Device generates false positive parity */
        unsigned int    irq_reroute_variant:2;  /* device needs IRQ rerouting variant */
-       unsigned int    msi_enabled:1;
+       unsigned int    msi_enabled:1;
        unsigned int    msix_enabled:1;
        unsigned int    ari_enabled:1;  /* ARI forwarding */
        unsigned int    is_managed:1;
@@ -371,7 +371,6 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
        if (dev->is_virtfn)
                dev = dev->physfn;
 #endif
-
        return dev;
 }
 
@@ -456,7 +455,7 @@ struct pci_bus {
        char            name[48];
 
        unsigned short  bridge_ctl;     /* manage NO_ISA/FBB/et al behaviors */
-       pci_bus_flags_t bus_flags;      /* Inherited by child busses */
+       pci_bus_flags_t bus_flags;      /* inherited by child buses */
        struct device           *bridge;
        struct device           dev;
        struct bin_attribute    *legacy_io; /* legacy I/O for this bus */
@@ -468,7 +467,7 @@ struct pci_bus {
 #define to_pci_bus(n)  container_of(n, struct pci_bus, dev)
 
 /*
- * Returns true if the pci bus is root (behind host-pci bridge),
+ * Returns true if the PCI bus is root (behind host-PCI bridge),
  * false otherwise
  *
  * Some code assumes that "bus->self == NULL" means that bus is a root bus.
@@ -510,7 +509,7 @@ static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) { return false;
 #define PCIBIOS_BUFFER_TOO_SMALL       0x89
 
 /*
- * Translate above to generic errno for passing back through non-pci.
+ * Translate above to generic errno for passing back through non-PCI code.
  */
 static inline int pcibios_err_to_errno(int err)
 {
@@ -561,11 +560,12 @@ struct pci_dynids {
        struct list_head list;      /* for IDs added at runtime */
 };
 
-/* ---------------------------------------------------------------- */
-/** PCI Error Recovery System (PCI-ERS).  If a PCI device driver provides
- *  a set of callbacks in struct pci_error_handlers, then that device driver
- *  will be notified of PCI bus errors, and will be driven to recovery
- *  when an error occurs.
+
+/*
+ * PCI Error Recovery System (PCI-ERS).  If a PCI device driver provides
+ * a set of callbacks in struct pci_error_handlers, that device driver
+ * will be notified of PCI bus errors, and will be driven to recovery
+ * when an error occurs.
  */
 
 typedef unsigned int __bitwise pci_ers_result_t;
@@ -609,7 +609,6 @@ struct pci_error_handlers {
        void (*resume)(struct pci_dev *dev);
 };
 
-/* ---------------------------------------------------------------- */
 
 struct module;
 struct pci_driver {
@@ -713,10 +712,10 @@ extern enum pcie_bus_config_types pcie_bus_config;
 
 extern struct bus_type pci_bus_type;
 
-/* Do NOT directly access these two variables, unless you are arch specific pci
- * code, or pci core code. */
+/* Do NOT directly access these two variables, unless you are arch-specific PCI
+ * code, or PCI core code. */
 extern struct list_head pci_root_buses;        /* list of all known PCI buses */
-/* Some device drivers need know if pci is initiated */
+/* Some device drivers need know if PCI is initiated */
 int no_pci_devices(void);
 
 void pcibios_resource_survey_bus(struct pci_bus *bus);
@@ -724,7 +723,7 @@ void pcibios_add_bus(struct pci_bus *bus);
 void pcibios_remove_bus(struct pci_bus *bus);
 void pcibios_fixup_bus(struct pci_bus *);
 int __must_check pcibios_enable_device(struct pci_dev *, int mask);
-/* Architecture specific versions may override this (weak) */
+/* Architecture-specific versions may override this (weak) */
 char *pcibios_setup(char *str);
 
 /* Used only when drivers/pci/setup.c is used */
@@ -1258,7 +1257,7 @@ void pci_cfg_access_unlock(struct pci_dev *dev);
 
 /*
  * PCI domain support.  Sometimes called PCI segment (eg by ACPI),
- * a PCI domain is defined to be a set of PCI busses which share
+ * a PCI domain is defined to be a set of PCI buses which share
  * configuration space.
  */
 #ifdef CONFIG_PCI_DOMAINS
@@ -1672,7 +1671,7 @@ extern u8 pci_cache_line_size;
 extern unsigned long pci_hotplug_io_size;
 extern unsigned long pci_hotplug_mem_size;
 
-/* Architecture specific versions may override these (weak) */
+/* Architecture-specific versions may override these (weak) */
 int pcibios_add_platform_entries(struct pci_dev *dev);
 void pcibios_disable_device(struct pci_dev *dev);
 void pcibios_set_master(struct pci_dev *dev);
index 430dd963707b43bdc995d9c5f1da353af9031665..a2e2f1d17e164c00b190d6515ec90950ef288e17 100644 (file)
@@ -39,8 +39,8 @@
  * @hardware_test: Called to run a specified hardware test on the specified
  * slot.
  * @get_power_status: Called to get the current power status of a slot.
- *     If this field is NULL, the value passed in the struct hotplug_slot_info
- *     will be used when this value is requested by a user.
+ *     If this field is NULL, the value passed in the struct hotplug_slot_info
+ *     will be used when this value is requested by a user.
  * @get_attention_status: Called to get the current attention status of a slot.
  *     If this field is NULL, the value passed in the struct hotplug_slot_info
  *     will be used when this value is requested by a user.
@@ -191,4 +191,3 @@ static inline int pci_get_hp_params(struct pci_dev *dev,
 
 void pci_configure_slot(struct pci_dev *dev);
 #endif
-
index 9572669eea9758003fba426128def92d6d211ef0..4f1089f2cc98b6b4690ac67aedce42ef4350e122 100644 (file)
@@ -23,7 +23,7 @@
 #define PCIE_PORT_SERVICE_VC           (1 << PCIE_PORT_SERVICE_VC_SHIFT)
 
 struct pcie_device {
-       int             irq;        /* Service IRQ/MSI/MSI-X Vector */
+       int             irq;        /* Service IRQ/MSI/MSI-X Vector */
        struct pci_dev *port;       /* Root/Upstream/Downstream Port */
        u32             service;    /* Port service this device represents */
        void            *priv_data; /* Service Private Data */
index 2e069d1288df5f04e6982243d7cac0f7455cd31d..8f4a70f2eca8bd0a3f55c572ad967ca516a2d8e1 100644 (file)
@@ -319,7 +319,10 @@ struct perf_event {
         */
        struct list_head                migrate_entry;
 
-       struct hlist_node               hlist_entry;
+       union {
+               struct hlist_node       hlist_entry;
+               struct list_head        active_entry;
+       };
        int                             nr_siblings;
        int                             group_flags;
        struct perf_event               *group_leader;
index 64ab823f7b7451f77aa4b58df22b4a3a54c30db1..48a4dc3cb8cf26b91af2bd2cb147bf6879956695 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 285025a9cdc9a0533e2fb61abbdc71b1808224e8..c42aa89d34eeb46ff0ec5f4e68229ad1fa0fad2a 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 179fb91bb5f2eaef7354e37411628f90a6c02335..f50821cb64be8c11165659b0f0719398bfa004cf 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 d44912d81578d072b8dab544254d58682ce39ab7..75f70f6ac13778f448a03de3136d32a0ef638b65 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 931bc616219f626562364ba289d55a84f2ef2ba0..d169820203dd825e54bd35479954e162509e78d8 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 56f4a866539ad7b66476fc3d1c09d15ab8ad2be1..2de2e275b2cbd1390ec6693d8f436b05352da16a 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 b122395bf5f4b58ea8cc1c1ce60ca4dd1282f7f2..bf14c215af1e820eb2395950701fc5b5831aa2c8 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>
@@ -168,7 +168,6 @@ extern char ___assert_task_state[1 - 2*!!(
 
 #define task_is_traced(task)   ((task->state & __TASK_TRACED) != 0)
 #define task_is_stopped(task)  ((task->state & __TASK_STOPPED) != 0)
-#define task_is_dead(task)     ((task)->exit_state != 0)
 #define task_is_stopped_or_traced(task)        \
                        ((task->state & (__TASK_STOPPED | __TASK_TRACED)) != 0)
 #define task_contributes_to_load(task) \
@@ -286,6 +285,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 9d37e2b9d3ec030f026117d99de94dfd50b32ab0..5623a7f965b7bbb07ab94a72351aec027ef4adb7 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 4e32edc8f506b89f13b0260200498b5cf87b490a..52e0097f61f00108f0faec9b37c57c2f03e1fcb2 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 1e8a8b6e837d8621fa5488f832273df673ff3520..cf87a24c0f92f1963081c93d7b0cce2356025358 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 215b5ea1cb302c43f0e8b9532fbe3ce59ff0f612..bec1cc7d5e3c41efbc4939ab742eeb955b64b7f0 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 74f105847d13ceae757c8aa6b83bdf8412816696..1e2f4fe12773bdaf9c5ffddd9b5eb77d42b665a0 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 */
@@ -381,10 +388,55 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
 /**
  * kmalloc - allocate memory
  * @size: how many bytes of memory are required.
- * @flags: the type of memory to allocate (see kcalloc).
+ * @flags: the type of memory to allocate.
  *
  * kmalloc is the normal method of allocating memory
  * for objects smaller than page size in the kernel.
+ *
+ * The @flags argument may be one of:
+ *
+ * %GFP_USER - Allocate memory on behalf of user.  May sleep.
+ *
+ * %GFP_KERNEL - Allocate normal kernel ram.  May sleep.
+ *
+ * %GFP_ATOMIC - Allocation will not sleep.  May use emergency pools.
+ *   For example, use this inside interrupt handlers.
+ *
+ * %GFP_HIGHUSER - Allocate pages from high memory.
+ *
+ * %GFP_NOIO - Do not do any I/O at all while trying to get memory.
+ *
+ * %GFP_NOFS - Do not make any fs calls while trying to get memory.
+ *
+ * %GFP_NOWAIT - Allocation will not sleep.
+ *
+ * %GFP_THISNODE - Allocate node-local memory only.
+ *
+ * %GFP_DMA - Allocation suitable for DMA.
+ *   Should only be used for kmalloc() caches. Otherwise, use a
+ *   slab created with SLAB_DMA.
+ *
+ * Also it is possible to set different flags by OR'ing
+ * in one or more of the following additional @flags:
+ *
+ * %__GFP_COLD - Request cache-cold pages instead of
+ *   trying to return cache-warm pages.
+ *
+ * %__GFP_HIGH - This allocation has high priority and may use emergency pools.
+ *
+ * %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail
+ *   (think twice before using).
+ *
+ * %__GFP_NORETRY - If memory is not immediately available,
+ *   then give up at once.
+ *
+ * %__GFP_NOWARN - If allocation fails, don't issue any warnings.
+ *
+ * %__GFP_REPEAT - If allocation fails initially, try once more before failing.
+ *
+ * There are other flags available as well, but these are not intended
+ * for general use, and so are not documented here. For a full list of
+ * potential flags, always refer to linux/gfp.h.
  */
 static __always_inline void *kmalloc(size_t size, gfp_t flags)
 {
@@ -494,61 +546,6 @@ struct seq_file;
 int cache_show(struct kmem_cache *s, struct seq_file *m);
 void print_slabinfo_header(struct seq_file *m);
 
-/**
- * kmalloc - allocate memory
- * @size: how many bytes of memory are required.
- * @flags: the type of memory to allocate.
- *
- * The @flags argument may be one of:
- *
- * %GFP_USER - Allocate memory on behalf of user.  May sleep.
- *
- * %GFP_KERNEL - Allocate normal kernel ram.  May sleep.
- *
- * %GFP_ATOMIC - Allocation will not sleep.  May use emergency pools.
- *   For example, use this inside interrupt handlers.
- *
- * %GFP_HIGHUSER - Allocate pages from high memory.
- *
- * %GFP_NOIO - Do not do any I/O at all while trying to get memory.
- *
- * %GFP_NOFS - Do not make any fs calls while trying to get memory.
- *
- * %GFP_NOWAIT - Allocation will not sleep.
- *
- * %GFP_THISNODE - Allocate node-local memory only.
- *
- * %GFP_DMA - Allocation suitable for DMA.
- *   Should only be used for kmalloc() caches. Otherwise, use a
- *   slab created with SLAB_DMA.
- *
- * Also it is possible to set different flags by OR'ing
- * in one or more of the following additional @flags:
- *
- * %__GFP_COLD - Request cache-cold pages instead of
- *   trying to return cache-warm pages.
- *
- * %__GFP_HIGH - This allocation has high priority and may use emergency pools.
- *
- * %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail
- *   (think twice before using).
- *
- * %__GFP_NORETRY - If memory is not immediately available,
- *   then give up at once.
- *
- * %__GFP_NOWARN - If allocation fails, don't issue any warnings.
- *
- * %__GFP_REPEAT - If allocation fails initially, try once more before failing.
- *
- * There are other flags available as well, but these are not intended
- * for general use, and so are not documented here. For a full list of
- * potential flags, always refer to linux/gfp.h.
- *
- * kmalloc is the normal method of allocating memory
- * in the kernel.
- */
-static __always_inline void *kmalloc(size_t size, gfp_t flags);
-
 /**
  * kmalloc_array - allocate memory for an array.
  * @n: number of elements.
index e9346b4f1ef4b2ef6d302a5799e30e631fa10ce2..09bfffb08a56db285caa27146202f04e2188b480 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 cc0b67eada4260331276e9ae145fa593c14ecee8..f56bfa9e4526f6382467fe2647c19bdbca185346 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 731f5237d5f4e0f87dd817e15983f509d3b4d483..5da22ee42e1635d35c5e70aa16e064af64e696b1 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 c114614ed172fdbb02b70fde61d6838f4734bfb4..9b058eecd40390b914d705809a1aabac3eff132b 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 8d4fa82bfb913d0021d93c49e71dab940804fdf7..c0f75261a728bf18b2ab930dbbee6bd35acf571c 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 c27f846f6b71ae10cc5a98de630f55e898b2c8e5..94273bbe605007462f6e0daacf01715f2a7aca6a 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 c98cfa40695248d16ab3ee91891c33334239c252..fd4498329c7c509b3614ad820c7c717d976afa2f 100644 (file)
@@ -45,6 +45,7 @@ struct clk;
 
 #define TEGRA_POWERGATE_3D0    TEGRA_POWERGATE_3D
 
+#ifdef CONFIG_ARCH_TEGRA
 int tegra_powergate_is_powered(int id);
 int tegra_powergate_power_on(int id);
 int tegra_powergate_power_off(int id);
@@ -52,5 +53,31 @@ int tegra_powergate_remove_clamping(int id);
 
 /* Must be called with clk disabled, and returns with clk enabled */
 int tegra_powergate_sequence_power_up(int id, struct clk *clk);
+#else
+static inline int tegra_powergate_is_powered(int id)
+{
+       return -ENOSYS;
+}
+
+static inline int tegra_powergate_power_on(int id)
+{
+       return -ENOSYS;
+}
+
+static inline int tegra_powergate_power_off(int id)
+{
+       return -ENOSYS;
+}
+
+static inline int tegra_powergate_remove_clamping(int id)
+{
+       return -ENOSYS;
+}
+
+static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk)
+{
+       return -ENOSYS;
+}
+#endif
 
 #endif /* _MACH_TEGRA_POWERGATE_H_ */
index ebeab360d851c1ece4952b3f5f4f294aa9673db0..f16dc0a4004976376c9036a244e1121ed52ed020 100644 (file)
@@ -267,6 +267,8 @@ static inline void tracepoint_synchronize_unregister(void)
 
 #define TRACE_EVENT_FLAGS(event, flag)
 
+#define TRACE_EVENT_PERF_PERM(event, expr...)
+
 #endif /* DECLARE_TRACE */
 
 #ifndef TRACE_EVENT
@@ -399,4 +401,6 @@ static inline void tracepoint_synchronize_unregister(void)
 
 #define TRACE_EVENT_FLAGS(event, flag)
 
+#define TRACE_EVENT_PERF_PERM(event, expr...)
+
 #endif /* ifdef TRACE_EVENT (see note above) */
index 319eae70fe8415f774e57c4861b98688fb81bb4c..e32251e00e62f0b2629c0ef7658d1445e6647586 100644 (file)
 
 #include <linux/errno.h>
 #include <linux/rbtree.h>
+#include <linux/types.h>
 
 struct vm_area_struct;
 struct mm_struct;
 struct inode;
 struct notifier_block;
 
-#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
-# include <asm/uprobes.h>
-#endif
-
 #define UPROBE_HANDLER_REMOVE          1
 #define UPROBE_HANDLER_MASK            1
 
@@ -60,6 +57,8 @@ struct uprobe_consumer {
 };
 
 #ifdef CONFIG_UPROBES
+#include <asm/uprobes.h>
+
 enum uprobe_task_state {
        UTASK_RUNNING,
        UTASK_SSTEP,
@@ -72,35 +71,28 @@ enum uprobe_task_state {
  */
 struct uprobe_task {
        enum uprobe_task_state          state;
-       struct arch_uprobe_task         autask;
 
-       struct return_instance          *return_instances;
-       unsigned int                    depth;
-       struct uprobe                   *active_uprobe;
+       union {
+               struct {
+                       struct arch_uprobe_task autask;
+                       unsigned long           vaddr;
+               };
 
+               struct {
+                       struct callback_head    dup_xol_work;
+                       unsigned long           dup_xol_addr;
+               };
+       };
+
+       struct uprobe                   *active_uprobe;
        unsigned long                   xol_vaddr;
-       unsigned long                   vaddr;
-};
 
-/*
- * On a breakpoint hit, thread contests for a slot.  It frees the
- * slot after singlestep. Currently a fixed number of slots are
- * allocated.
- */
-struct xol_area {
-       wait_queue_head_t       wq;             /* if all slots are busy */
-       atomic_t                slot_count;     /* number of in-use slots */
-       unsigned long           *bitmap;        /* 0 = free slot */
-       struct page             *page;
-
-       /*
-        * We keep the vma's vm_start rather than a pointer to the vma
-        * itself.  The probed process or a naughty kernel module could make
-        * the vma go away, and we must handle that reasonably gracefully.
-        */
-       unsigned long           vaddr;          /* Page(s) of instruction slots */
+       struct return_instance          *return_instances;
+       unsigned int                    depth;
 };
 
+struct xol_area;
+
 struct uprobes_state {
        struct xol_area         *xol_area;
 };
@@ -109,6 +101,7 @@ extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsign
 extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr);
 extern bool __weak is_swbp_insn(uprobe_opcode_t *insn);
 extern bool __weak is_trap_insn(uprobe_opcode_t *insn);
+extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
 extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
 extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
 extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool);
@@ -120,7 +113,6 @@ extern void uprobe_end_dup_mmap(void);
 extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
 extern void uprobe_free_utask(struct task_struct *t);
 extern void uprobe_copy_process(struct task_struct *t, unsigned long flags);
-extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
 extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
 extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
 extern void uprobe_notify_resume(struct pt_regs *regs);
@@ -176,10 +168,6 @@ static inline bool uprobe_deny_signal(void)
 {
        return false;
 }
-static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
-{
-       return 0;
-}
 static inline void uprobe_free_utask(struct task_struct *t)
 {
 }
index 4db29859464f3af3d78caafa41a1e7525034a589..4836ba3c1cd8266c294b9dfd28aa6d0d433db0d6 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 36d36cc893295387bb0bb841218eb0021c0ab59d..e4abb84199bea7599940a4fe4ee026ee40f6a48d 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 29b9104232b40c038bf29969d9c63795e8575fce..e8f8f71e843c96a1a8b7473e3a0f0b3b0334a458 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 b300787af8e089b05e4038d71fef277fba9f85e4..67e06fe18c03b6814e217f05134cdb68a5df13bc 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 61939ba30aa0abdbe7e36e608343338c80fada6b..eaa00b10abaaa53cf441170841c3faec588e9de0 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 34d2414f2b8c0a6207fb3d4dfe2c34d644f7d6aa..865246b001279ec611b5c7c8b3d23b194217d6f1 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 0503a90b48bb2fdab6bfbf4395647cdc599027df..0b36cc138304706e98e5993fc0014b4af9afda43 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 16550c4390081a03a8f2d4c8d3fdc51de54cc006..48f974866f13fe2fd2362ed0c87ccd8c29d59811 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 47ada23345a195973c3942681566244d50c7f34d..16f7f2606516982d8cf60ef18c3c953e15ac0526 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 a62ee18cb7b799c3982decdb63dc57f520e9b93a..528cdaf622e192aa572ade990b60acf949e74109 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 bfda0fe9aeb05fc494a167cc246d9b500a508be2..d67210a37ef376192dcd70e5004c4b526ea753e8 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 6781258d0b677f4a7092b4d29d0c1c7094e8a8a0..bd8218b15009a810af8abd5df5a4bcad31dfdb20 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 0038526b8ef7c0495bf12784a6c3c7a855c0b144..7b89852779af77b0f156f635f7b8e5287d9a9be1 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 51329905bfaafda1c290a26103914d5879144a01..c853b16de4efde38a501440ce58ca260a379e5da 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 9b787b62cf16066e6f2335301865849fa674f7a1..1b177ed803b7aa99045fec46e6449b29b76ac323 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 e393171e2facd2ad6317783560ceae6f89f80763..979874c627ee7f450c7aabff5c948cb2c87f84c3 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 ff0f04ac91aad7c2311f9b889aa09c0accb90cc6..4ebf6913b7b2e4b5f85ffca6c2963331e2f433a0 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 5ebe21cd5d1cda075c484f13fd818e289ff0932c..39e0114d70c54cba70f7180c8c9763529b91ef0f 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 5bdb8b7d2a69e52b56a6fe98dcd8aeb632cbc018..45412a6afa69d95ea82c820fb197d350c9d0575e 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 713c5004f4ae8e559349990d72da0d710ae4e44b..e0801386e4dcb793bfa0f06b4052ad1c517fa725 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 882b650e32be1d51b9945f4c475e5adcc79b523a..4cf4fda404a3ce80b31d02b0b04c209cbb1ca0f0 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 5ebda976ea93bd52c6ca80194d723e49e94c1558..e2b9576d00e24772580a601a268e42bdc46e349e 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 f18b3b76e01e22e00c00ee133b2ebdc1013bcc62..4832d75dcbaedb888a2751303fe7d5a4e62b8010 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 7005d1109ec94c8c2839bbff6984395e20e8a873..131a0bda7aecec634b61ac72078d07d31eb1602c 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 422df19de732b687da6fbce4a54f90e48a6aae44..805af6db41cc6091a877d8ddf58742cb2e3fce55 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 5c7ab17cbb0225eb3147d108a38d2564f5f23db8..5c38606613d89a06c361a56054e97031c391c2d3 100644 (file)
 #define TRACE_EVENT_FLAGS(name, value)                                 \
        __TRACE_EVENT_FLAGS(name, value)
 
+#undef TRACE_EVENT_PERF_PERM
+#define TRACE_EVENT_PERF_PERM(name, expr...)                           \
+       __TRACE_EVENT_PERF_PERM(name, expr)
+
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
 
 
 #undef TRACE_EVENT_FLAGS
 #define TRACE_EVENT_FLAGS(event, flag)
 
+#undef TRACE_EVENT_PERF_PERM
+#define TRACE_EVENT_PERF_PERM(event, expr...)
+
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
 
 /*
@@ -372,7 +379,8 @@ ftrace_define_fields_##call(struct ftrace_event_call *event_call)   \
        __data_size += (len) * sizeof(type);
 
 #undef __string
-#define __string(item, src) __dynamic_array(char, item, strlen(src) + 1)
+#define __string(item, src) __dynamic_array(char, item,                        \
+                   strlen((src) ? (const char *)(src) : "(null)") + 1)
 
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
@@ -437,9 +445,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> = {
@@ -502,7 +509,7 @@ static inline notrace int ftrace_get_offsets_##call(                        \
 
 #undef __assign_str
 #define __assign_str(dst, src)                                         \
-       strcpy(__get_str(dst), src);
+       strcpy(__get_str(dst), (src) ? (const char *)(src) : "(null)");
 
 #undef TP_fast_assign
 #define TP_fast_assign(args...) args
@@ -553,7 +560,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 ece867889cc789ef3d6a467e535723911ac5d1d4..9b24d65fed72b9d06843cba89ebeeabff07fb71f 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 28acbaf4a81ec091a54740c54adfd57ec2f9ad59..f104c2603ebe44397d62a45c50e39e18c5ae2b2e 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 55bb5729bd78a534da094f07fe74714e162a2c9c..3a4e97bd860771b6ad8e3b5d026a35200fcad4d3 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 46d41e8b0dccec30ec5b52f6dc772bf9e3088dc6..2f3f7ea8c77b8a653b0972302ead53319f13e5fc 100644 (file)
@@ -981,6 +981,8 @@ struct drm_radeon_cs {
 #define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16
 /* query if CP DMA is supported on the compute ring */
 #define RADEON_INFO_SI_CP_DMA_COMPUTE  0x17
+/* CIK macrotile mode array */
+#define RADEON_INFO_CIK_MACROTILE_MODE_ARRAY   0x18
 
 
 struct drm_radeon_info {
index 73bde4eaf16c2042038ce6435072ab8d39632e41..5e1ab552cbed31bacae535f03b6c43431af87d52 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 db0b825b48109f2e8cc011f211749043757a33e5..44b05a09f1933a1c293a2174eb58d4db7b473b80 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 c880a417d8a93693874b76a3e364a203ed4d0a9c..1af72d8228e04db8c1d6de05cacfa1bfd4a577b7 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 c9b7f4faf97aa5a790f6b58fd6d9da25ca2ebd64..840cb990abe2e7147ec89c92ef143e86a35ce0f8 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 99c25338ede88c755698fe892983665decb005bc..902f124618738ae9950ffdfaa6ed4f5675be1cc5 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 2944278a8ba7e933e64c53c4cfe593bb9a328b84..77c60311a6c6224b66e7c35a712d1b113258e27d 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 0890556f779e489c16aa57490f3d4fef97e7e86e..4a98e85438a7706c5288deda0626d17ed312aa17 100644 (file)
  *     PCI to PCI Bridge Specification
  *     PCI System Design Guide
  *
- *     For hypertransport information, please consult the following manuals
- *     from http://www.hypertransport.org
+ *     For HyperTransport information, please consult the following manuals
+ *     from http://www.hypertransport.org
  *
- *     The Hypertransport I/O Link Specification
+ *     The HyperTransport I/O Link Specification
  */
 
 #ifndef LINUX_PCI_REGS_H
@@ -37,7 +37,7 @@
 #define  PCI_COMMAND_INVALIDATE        0x10    /* Use memory write and invalidate */
 #define  PCI_COMMAND_VGA_PALETTE 0x20  /* Enable palette snooping */
 #define  PCI_COMMAND_PARITY    0x40    /* Enable parity checking */
-#define  PCI_COMMAND_WAIT      0x80    /* Enable address/data stepping */
+#define  PCI_COMMAND_WAIT      0x80    /* Enable address/data stepping */
 #define  PCI_COMMAND_SERR      0x100   /* Enable SERR */
 #define  PCI_COMMAND_FAST_BACK 0x200   /* Enable back-to-back writes */
 #define  PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
@@ -45,7 +45,7 @@
 #define PCI_STATUS             0x06    /* 16 bits */
 #define  PCI_STATUS_INTERRUPT  0x08    /* Interrupt status */
 #define  PCI_STATUS_CAP_LIST   0x10    /* Support Capability List */
-#define  PCI_STATUS_66MHZ      0x20    /* Support 66 Mhz PCI 2.1 bus */
+#define  PCI_STATUS_66MHZ      0x20    /* Support 66 MHz PCI 2.1 bus */
 #define  PCI_STATUS_UDF                0x40    /* Support User Definable Features [obsolete] */
 #define  PCI_STATUS_FAST_BACK  0x80    /* Accept fast-back to back */
 #define  PCI_STATUS_PARITY     0x100   /* Detected parity error */
 #define  PCI_CAP_ID_CHSWP      0x06    /* CompactPCI HotSwap */
 #define  PCI_CAP_ID_PCIX       0x07    /* PCI-X */
 #define  PCI_CAP_ID_HT         0x08    /* HyperTransport */
-#define  PCI_CAP_ID_VNDR       0x09    /* Vendor specific */
+#define  PCI_CAP_ID_VNDR       0x09    /* Vendor-Specific */
 #define  PCI_CAP_ID_DBG                0x0A    /* Debug port */
 #define  PCI_CAP_ID_CCRC       0x0B    /* CompactPCI Central Resource Control */
-#define  PCI_CAP_ID_SHPC       0x0C    /* PCI Standard Hot-Plug Controller */
+#define  PCI_CAP_ID_SHPC       0x0C    /* PCI Standard Hot-Plug Controller */
 #define  PCI_CAP_ID_SSVID      0x0D    /* Bridge subsystem vendor/device ID */
 #define  PCI_CAP_ID_AGP3       0x0E    /* AGP Target PCI-PCI bridge */
 #define  PCI_CAP_ID_SECDEV     0x0F    /* Secure Device */
-#define  PCI_CAP_ID_EXP        0x10    /* PCI Express */
+#define  PCI_CAP_ID_EXP                0x10    /* PCI Express */
 #define  PCI_CAP_ID_MSIX       0x11    /* MSI-X */
 #define  PCI_CAP_ID_SATA       0x12    /* SATA Data/Index Conf. */
 #define  PCI_CAP_ID_AF         0x13    /* PCI Advanced Features */
 #define  PCI_AGP_COMMAND_RQ_MASK 0xff000000  /* Master: Maximum number of requests */
 #define  PCI_AGP_COMMAND_SBA   0x0200  /* Sideband addressing enabled */
 #define  PCI_AGP_COMMAND_AGP   0x0100  /* Allow processing of AGP transactions */
-#define  PCI_AGP_COMMAND_64BIT 0x0020  /* Allow processing of 64-bit addresses */
-#define  PCI_AGP_COMMAND_FW    0x0010  /* Force FW transfers */
+#define  PCI_AGP_COMMAND_64BIT 0x0020  /* Allow processing of 64-bit addresses */
+#define  PCI_AGP_COMMAND_FW    0x0010  /* Force FW transfers */
 #define  PCI_AGP_COMMAND_RATE4 0x0004  /* Use 4x rate */
 #define  PCI_AGP_COMMAND_RATE2 0x0002  /* Use 2x rate */
 #define  PCI_AGP_COMMAND_RATE1 0x0001  /* Use 1x rate */
 #define  PCI_MSIX_PBA_OFFSET   0xfffffff8 /* Offset into specified BAR */
 #define PCI_CAP_MSIX_SIZEOF    12      /* size of MSIX registers */
 
-/* MSI-X entry's format */
+/* MSI-X Table entry format */
 #define PCI_MSIX_ENTRY_SIZE            16
 #define  PCI_MSIX_ENTRY_LOWER_ADDR     0
 #define  PCI_MSIX_ENTRY_UPPER_ADDR     4
 #define  PCI_X_CMD_SPLIT_16    0x0060  /* Max 16 */
 #define  PCI_X_CMD_SPLIT_32    0x0070  /* Max 32 */
 #define  PCI_X_CMD_MAX_SPLIT   0x0070  /* Max Outstanding Split Transactions */
-#define  PCI_X_CMD_VERSION(x)  (((x) >> 12) & 3) /* Version */
+#define  PCI_X_CMD_VERSION(x)  (((x) >> 12) & 3) /* Version */
 #define PCI_X_STATUS           4       /* PCI-X capabilities */
 #define  PCI_X_STATUS_DEVFN    0x000000ff      /* A copy of devfn */
 #define  PCI_X_STATUS_BUS      0x0000ff00      /* A copy of bus nr */
 
 /* PCI Bridge Subsystem ID registers */
 
-#define PCI_SSVID_VENDOR_ID     4      /* PCI-Bridge subsystem vendor id register */
-#define PCI_SSVID_DEVICE_ID     6      /* PCI-Bridge subsystem device id register */
+#define PCI_SSVID_VENDOR_ID     4      /* PCI Bridge subsystem vendor ID */
+#define PCI_SSVID_DEVICE_ID     6      /* PCI Bridge subsystem device ID */
 
 /* PCI Express capability registers */
 
 #define  PCI_EXP_LNKCTL_CLKREQ_EN 0x0100 /* Enable clkreq */
 #define  PCI_EXP_LNKCTL_HAWD   0x0200  /* Hardware Autonomous Width Disable */
 #define  PCI_EXP_LNKCTL_LBMIE  0x0400  /* Link Bandwidth Management Interrupt Enable */
-#define  PCI_EXP_LNKCTL_LABIE  0x0800  /* Lnk Autonomous Bandwidth Interrupt Enable */
+#define  PCI_EXP_LNKCTL_LABIE  0x0800  /* Link Autonomous Bandwidth Interrupt Enable */
 #define PCI_EXP_LNKSTA         18      /* Link Status */
 #define  PCI_EXP_LNKSTA_CLS    0x000f  /* Current Link Speed */
 #define  PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */
 #define  PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */
-#define  PCI_EXP_LNKSTA_NLW    0x03f0  /* Nogotiated Link Width */
+#define  PCI_EXP_LNKSTA_NLW    0x03f0  /* Negotiated Link Width */
 #define  PCI_EXP_LNKSTA_NLW_SHIFT 4    /* start of NLW mask in link status */
 #define  PCI_EXP_LNKSTA_LT     0x0800  /* Link Training */
 #define  PCI_EXP_LNKSTA_SLC    0x1000  /* Slot Clock Configuration */
 #define PCI_EXT_CAP_ID_MFVC    0x08    /* Multi-Function VC Capability */
 #define PCI_EXT_CAP_ID_VC9     0x09    /* same as _VC */
 #define PCI_EXT_CAP_ID_RCRB    0x0A    /* Root Complex RB? */
-#define PCI_EXT_CAP_ID_VNDR    0x0B    /* Vendor Specific */
+#define PCI_EXT_CAP_ID_VNDR    0x0B    /* Vendor-Specific */
 #define PCI_EXT_CAP_ID_CAC     0x0C    /* Config Access - obsolete */
 #define PCI_EXT_CAP_ID_ACS     0x0D    /* Access Control Services */
 #define PCI_EXT_CAP_ID_ARI     0x0E    /* Alternate Routing ID */
 #define PCI_EXT_CAP_ID_MRIOV   0x11    /* Multi Root I/O Virtualization */
 #define PCI_EXT_CAP_ID_MCAST   0x12    /* Multicast */
 #define PCI_EXT_CAP_ID_PRI     0x13    /* Page Request Interface */
-#define PCI_EXT_CAP_ID_AMD_XXX 0x14    /* reserved for AMD */
-#define PCI_EXT_CAP_ID_REBAR   0x15    /* resizable BAR */
-#define PCI_EXT_CAP_ID_DPA     0x16    /* dynamic power alloc */
-#define PCI_EXT_CAP_ID_TPH     0x17    /* TPH request */
-#define PCI_EXT_CAP_ID_LTR     0x18    /* latency tolerance reporting */
-#define PCI_EXT_CAP_ID_SECPCI  0x19    /* Secondary PCIe */
+#define PCI_EXT_CAP_ID_AMD_XXX 0x14    /* Reserved for AMD */
+#define PCI_EXT_CAP_ID_REBAR   0x15    /* Resizable BAR */
+#define PCI_EXT_CAP_ID_DPA     0x16    /* Dynamic Power Allocation */
+#define PCI_EXT_CAP_ID_TPH     0x17    /* TPH Requester */
+#define PCI_EXT_CAP_ID_LTR     0x18    /* Latency Tolerance Reporting */
+#define PCI_EXT_CAP_ID_SECPCI  0x19    /* Secondary PCIe Capability */
 #define PCI_EXT_CAP_ID_PMUX    0x1A    /* Protocol Multiplexing */
 #define PCI_EXT_CAP_ID_PASID   0x1B    /* Process Address Space ID */
 #define PCI_EXT_CAP_ID_MAX     PCI_EXT_CAP_ID_PASID
 #define PCI_ERR_ROOT_COR_RCV           0x00000001      /* ERR_COR Received */
 /* Multi ERR_COR Received */
 #define PCI_ERR_ROOT_MULTI_COR_RCV     0x00000002
-/* ERR_FATAL/NONFATAL Recevied */
+/* ERR_FATAL/NONFATAL Received */
 #define PCI_ERR_ROOT_UNCOR_RCV         0x00000004
-/* Multi ERR_FATAL/NONFATAL Recevied */
+/* Multi ERR_FATAL/NONFATAL Received */
 #define PCI_ERR_ROOT_MULTI_UNCOR_RCV   0x00000008
 #define PCI_ERR_ROOT_FIRST_FATAL       0x00000010      /* First Fatal */
 #define PCI_ERR_ROOT_NONFATAL_RCV      0x00000020      /* Non-Fatal Received */
 
 /* Virtual Channel */
 #define PCI_VC_PORT_REG1       4
-#define  PCI_VC_REG1_EVCC      0x7     /* extended vc count */
+#define  PCI_VC_REG1_EVCC      0x7     /* extended VC count */
 #define PCI_VC_PORT_REG2       8
 #define  PCI_VC_REG2_32_PHASE  0x2
 #define  PCI_VC_REG2_64_PHASE  0x4
 #define  PCI_VNDR_HEADER_LEN(x)        (((x) >> 20) & 0xfff)
 
 /*
- * Hypertransport sub capability types
+ * HyperTransport sub capability types
  *
  * Unfortunately there are both 3 bit and 5 bit capability types defined
  * in the HT spec, catering for that is a little messy. You probably don't
 #define HT_CAPTYPE_DIRECT_ROUTE        0xB0    /* Direct routing configuration */
 #define HT_CAPTYPE_VCSET       0xB8    /* Virtual Channel configuration */
 #define HT_CAPTYPE_ERROR_RETRY 0xC0    /* Retry on error configuration */
-#define HT_CAPTYPE_GEN3                0xD0    /* Generation 3 hypertransport configuration */
-#define HT_CAPTYPE_PM          0xE0    /* Hypertransport powermanagement configuration */
+#define HT_CAPTYPE_GEN3                0xD0    /* Generation 3 HyperTransport configuration */
+#define HT_CAPTYPE_PM          0xE0    /* HyperTransport power management configuration */
 #define HT_CAP_SIZEOF_LONG     28      /* slave & primary */
 #define HT_CAP_SIZEOF_SHORT    24      /* host & secondary */
 
 #define PCI_PRI_ALLOC_REQ      0x0c    /* PRI max reqs allowed */
 #define PCI_EXT_CAP_PRI_SIZEOF 16
 
-/* PASID capability */
+/* Process Address Space ID */
 #define PCI_PASID_CAP          0x04    /* PASID feature register */
 #define  PCI_PASID_CAP_EXEC    0x02    /* Exec permissions Supported */
-#define  PCI_PASID_CAP_PRIV    0x04    /* Priviledge Mode Supported */
+#define  PCI_PASID_CAP_PRIV    0x04    /* Privilege Mode Supported */
 #define PCI_PASID_CTRL         0x06    /* PASID control register */
 #define  PCI_PASID_CTRL_ENABLE 0x01    /* Enable bit */
 #define  PCI_PASID_CTRL_EXEC   0x02    /* Exec permissions Enable */
-#define  PCI_PASID_CTRL_PRIV   0x04    /* Priviledge Mode Enable */
+#define  PCI_PASID_CTRL_PRIV   0x04    /* Privilege Mode Enable */
 #define PCI_EXT_CAP_PASID_SIZEOF       8
 
 /* Single Root I/O Virtualization */
 #define PCI_ACS_CTRL           0x06    /* ACS Control Register */
 #define PCI_ACS_EGRESS_CTL_V   0x08    /* ACS Egress Control Vector */
 
-#define PCI_VSEC_HDR           4       /* extended cap - vendor specific */
+#define PCI_VSEC_HDR           4       /* extended cap - vendor-specific */
 #define  PCI_VSEC_HDR_LEN_SHIFT        20      /* shift for length field */
 
-/* sata capability */
+/* SATA capability */
 #define PCI_SATA_REGS          4       /* SATA REGs specifier */
 #define  PCI_SATA_REGS_MASK    0xF     /* location - BAR#/inline */
 #define  PCI_SATA_REGS_INLINE  0xF     /* REGS in config space */
 #define PCI_SATA_SIZEOF_SHORT  8
 #define PCI_SATA_SIZEOF_LONG   16
 
-/* resizable BARs */
+/* Resizable BARs */
 #define PCI_REBAR_CTRL         8       /* control register */
 #define  PCI_REBAR_CTRL_NBAR_MASK      (7 << 5)        /* mask for # bars */
 #define  PCI_REBAR_CTRL_NBAR_SHIFT     5       /* shift for # bars */
 
-/* dynamic power allocation */
+/* Dynamic Power Allocation */
 #define PCI_DPA_CAP            4       /* capability register */
 #define  PCI_DPA_CAP_SUBSTATE_MASK     0x1F    /* # substates - 1 */
 #define PCI_DPA_BASE_SIZEOF    16      /* size with 0 substates */
index 307f293477e83684970fc5c6dc05225d750d0882..a806687ad98fc1258ae0b36c75164ecf5d59eec4 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 fe1a5406d4d93cdf6012d11ab7315f014b552ede..f7cf7f351144873efd0bde3bdfe4e8f1676eb41d 100644 (file)
@@ -16,6 +16,7 @@
 #define _MD_P_H
 
 #include <linux/types.h>
+#include <asm/byteorder.h>
 
 /*
  * RAID superblock.
index 083bb5a5aae22f23e4e7c42c73714ee6df855c8b..1666aabbbb868ae9a215794e36726bd37a6fe2bd 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 e3ddd86c90a68610a20625729a99cf8effea8f31..cbfdd4ca951021854ff70843143ec8e1fabda358 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 7000bb1f6e96fb018fcc5360cf6266d567f6121e..42721d13a106050b9d35bf4e2aa411c57af8e802 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 de8bcc641c49ae64c6971c11b792b696216cce0a..8b2eb93ae8ba89efc5bbd89b690ff1046b089128 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 d6fe062cad6b3128b25104107a1c0468d442295f..fb2ea8f26552bdc4f093aaf1cff9b6485d888b98 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 5496f307988ee44603ead02db2b2c5caa48511bd..79383d3aa5dc5f7fe64abdcaf7d511144ca016e5 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 6ad1a533a8c7fc439ccadfd7f87c23d4e32b1ff5..febc511e078a65d08ac4dd6a3be67b26926195cd 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 d69739610fd4384323004c46782116d54db6bbd5..7a51443a51d6421bd2a02a66ec18db98f6796dd3 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 94fabd534b03d9347dc117b653204fa838283def..2a202a846757f238cccb290033633e5778bd3bb9 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 09a9c94f42bde841a58b875ca7fc75c1b69f65c6..bbaf7d59c1bb14f166441e6532b55585790e4b52 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 7b0e23a740ce345987c33f9e012302c24de0f4db..906ae5a0233a1011d558ff47c548808517ef9a03 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 123c9b7c39795975e2ce18cf913aad6b1fbe5b85..b779642b29af9401fd15246816c376ec86e4fcab 100644 (file)
@@ -197,6 +197,9 @@ struct audit_context {
                        int                     fd;
                        int                     flags;
                } mmap;
+               struct {
+                       int                     argc;
+               } execve;
        };
        int fds[2];
 
index f7aee8be7fb286db4a40919bc10ede18f17016dc..51f3fd4c1ed3a71dd1b8cd95e6645221150f5047 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 9845cb32b60a77c8f6e3a7f1e94c8aec9dc599b2..90594c9f755213232e5282899c8547cd5c61c823 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 e8ca97b5c386dc8a7e5374ab0aae48091fc30b6d..5253204afdcace55e89cda669aaf2896eba23aaa 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 e0839bcd48c8c2fdce9c13a3bfeba35a8e5f0e75..8b729c278b649c7c016786f6dac9886290ce794f 100644 (file)
@@ -89,6 +89,14 @@ static DEFINE_MUTEX(cgroup_mutex);
 
 static DEFINE_MUTEX(cgroup_root_mutex);
 
+/*
+ * cgroup destruction makes heavy use of work items and there can be a lot
+ * of concurrent destructions.  Use a separate workqueue so that cgroup
+ * destruction work items don't end up filling up max_active of system_wq
+ * which may lead to deadlock.
+ */
+static struct workqueue_struct *cgroup_destroy_wq;
+
 /*
  * Generate an array of cgroup subsystem pointers. At boot time, this is
  * populated with the built in subsystems, and modular subsystems are
@@ -191,6 +199,7 @@ static void cgroup_destroy_css_killed(struct cgroup *cgrp);
 static int cgroup_destroy_locked(struct cgroup *cgrp);
 static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
                              bool is_add);
+static int cgroup_file_release(struct inode *inode, struct file *file);
 
 /**
  * cgroup_css - obtain a cgroup's css for the specified subsystem
@@ -871,7 +880,7 @@ static void cgroup_free_rcu(struct rcu_head *head)
        struct cgroup *cgrp = container_of(head, struct cgroup, rcu_head);
 
        INIT_WORK(&cgrp->destroy_work, cgroup_free_fn);
-       schedule_work(&cgrp->destroy_work);
+       queue_work(cgroup_destroy_wq, &cgrp->destroy_work);
 }
 
 static void cgroup_diput(struct dentry *dentry, struct inode *inode)
@@ -895,11 +904,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 +1490,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 =
@@ -2426,7 +2430,7 @@ static const struct file_operations cgroup_seqfile_operations = {
        .read = seq_read,
        .write = cgroup_file_write,
        .llseek = seq_lseek,
-       .release = single_release,
+       .release = cgroup_file_release,
 };
 
 static int cgroup_file_open(struct inode *inode, struct file *file)
@@ -2487,6 +2491,8 @@ static int cgroup_file_release(struct inode *inode, struct file *file)
                ret = cft->release(inode, file);
        if (css->ss)
                css_put(css);
+       if (file->f_op == &cgroup_seqfile_operations)
+               single_release(inode, file);
        return ret;
 }
 
@@ -4254,7 +4260,7 @@ static void css_free_rcu_fn(struct rcu_head *rcu_head)
         * css_put().  dput() requires process context which we don't have.
         */
        INIT_WORK(&css->destroy_work, css_free_work_fn);
-       schedule_work(&css->destroy_work);
+       queue_work(cgroup_destroy_wq, &css->destroy_work);
 }
 
 static void css_release(struct percpu_ref *ref)
@@ -4544,7 +4550,7 @@ static void css_killed_ref_fn(struct percpu_ref *ref)
                container_of(ref, struct cgroup_subsys_state, refcnt);
 
        INIT_WORK(&css->destroy_work, css_killed_work_fn);
-       schedule_work(&css->destroy_work);
+       queue_work(cgroup_destroy_wq, &css->destroy_work);
 }
 
 /**
@@ -5068,6 +5074,22 @@ out:
        return err;
 }
 
+static int __init cgroup_wq_init(void)
+{
+       /*
+        * There isn't much point in executing destruction path in
+        * parallel.  Good chunk is serialized with cgroup_mutex anyway.
+        * Use 1 for @max_active.
+        *
+        * We would prefer to do this in cgroup_init() above, but that
+        * is called before init_workqueues(): so leave this until after.
+        */
+       cgroup_destroy_wq = alloc_workqueue("cgroup_destroy", 0, 1);
+       BUG_ON(!cgroup_destroy_wq);
+       return 0;
+}
+core_initcall(cgroup_wq_init);
+
 /*
  * proc_cgroup_show()
  *  - Print task's cgroup paths into seq_file, one line for each hierarchy
index 6bf981e13c437ff81979f4c9f08f69998c8ca45a..4772034b4b17062a4506bc4f437c599b8d5f2ac3 100644 (file)
@@ -1033,8 +1033,10 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk,
        need_loop = task_has_mempolicy(tsk) ||
                        !nodes_intersects(*newmems, tsk->mems_allowed);
 
-       if (need_loop)
+       if (need_loop) {
+               local_irq_disable();
                write_seqcount_begin(&tsk->mems_allowed_seq);
+       }
 
        nodes_or(tsk->mems_allowed, tsk->mems_allowed, *newmems);
        mpol_rebind_task(tsk, newmems, MPOL_REBIND_STEP1);
@@ -1042,8 +1044,10 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk,
        mpol_rebind_task(tsk, newmems, MPOL_REBIND_STEP2);
        tsk->mems_allowed = *newmems;
 
-       if (need_loop)
+       if (need_loop) {
                write_seqcount_end(&tsk->mems_allowed_seq);
+               local_irq_enable();
+       }
 
        task_unlock(tsk);
 }
index d724e7757cd1ec2df7a9c2ac9be1b0c23f6e4e71..403b781daafb694ef9baa94e5a176212a23678e1 100644 (file)
@@ -5680,11 +5680,6 @@ static void swevent_hlist_put(struct perf_event *event)
 {
        int cpu;
 
-       if (event->cpu != -1) {
-               swevent_hlist_put_cpu(event, event->cpu);
-               return;
-       }
-
        for_each_possible_cpu(cpu)
                swevent_hlist_put_cpu(event, cpu);
 }
@@ -5718,9 +5713,6 @@ static int swevent_hlist_get(struct perf_event *event)
        int err;
        int cpu, failed_cpu;
 
-       if (event->cpu != -1)
-               return swevent_hlist_get_cpu(event, event->cpu);
-
        get_online_cpus();
        for_each_possible_cpu(cpu) {
                err = swevent_hlist_get_cpu(event, cpu);
@@ -6663,6 +6655,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
        INIT_LIST_HEAD(&event->event_entry);
        INIT_LIST_HEAD(&event->sibling_list);
        INIT_LIST_HEAD(&event->rb_entry);
+       INIT_LIST_HEAD(&event->active_entry);
 
        init_waitqueue_head(&event->waitq);
        init_irq_work(&event->pending, perf_pending_event);
index 24b7d6ca871b632f2e35912e720bfc0162c939c5..b886a5e7d4ff4a5d1c7b240a63aed39538926de4 100644 (file)
@@ -73,6 +73,17 @@ struct uprobe {
        struct inode            *inode;         /* Also hold a ref to inode */
        loff_t                  offset;
        unsigned long           flags;
+
+       /*
+        * The generic code assumes that it has two members of unknown type
+        * owned by the arch-specific code:
+        *
+        *      insn -  copy_insn() saves the original instruction here for
+        *              arch_uprobe_analyze_insn().
+        *
+        *      ixol -  potentially modified instruction to execute out of
+        *              line, copied to xol_area by xol_get_insn_slot().
+        */
        struct arch_uprobe      arch;
 };
 
@@ -85,6 +96,29 @@ struct return_instance {
        struct return_instance  *next;          /* keep as stack */
 };
 
+/*
+ * Execute out of line area: anonymous executable mapping installed
+ * by the probed task to execute the copy of the original instruction
+ * mangled by set_swbp().
+ *
+ * On a breakpoint hit, thread contests for a slot.  It frees the
+ * slot after singlestep. Currently a fixed number of slots are
+ * allocated.
+ */
+struct xol_area {
+       wait_queue_head_t       wq;             /* if all slots are busy */
+       atomic_t                slot_count;     /* number of in-use slots */
+       unsigned long           *bitmap;        /* 0 = free slot */
+       struct page             *page;
+
+       /*
+        * We keep the vma's vm_start rather than a pointer to the vma
+        * itself.  The probed process or a naughty kernel module could make
+        * the vma go away, and we must handle that reasonably gracefully.
+        */
+       unsigned long           vaddr;          /* Page(s) of instruction slots */
+};
+
 /*
  * valid_vma: Verify if the specified vma is an executable vma
  * Relax restrictions while unregistering: vm_flags might have
@@ -330,7 +364,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
 int __weak
 set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-       return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
+       return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)&auprobe->insn);
 }
 
 static int match_uprobe(struct uprobe *l, struct uprobe *r)
@@ -529,8 +563,8 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp)
 {
        struct address_space *mapping = uprobe->inode->i_mapping;
        loff_t offs = uprobe->offset;
-       void *insn = uprobe->arch.insn;
-       int size = MAX_UINSN_BYTES;
+       void *insn = &uprobe->arch.insn;
+       int size = sizeof(uprobe->arch.insn);
        int len, err = -EIO;
 
        /* Copy only available bytes, -EIO if nothing was read */
@@ -569,7 +603,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file,
                goto out;
 
        ret = -ENOTSUPP;
-       if (is_trap_insn((uprobe_opcode_t *)uprobe->arch.insn))
+       if (is_trap_insn((uprobe_opcode_t *)&uprobe->arch.insn))
                goto out;
 
        ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr);
@@ -1264,7 +1298,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 
        /* Initialize the slot */
        copy_to_page(area->page, xol_vaddr,
-                       uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
+                       &uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
        /*
         * We probably need flush_icache_user_range() but it needs vma.
         * This should work on supported architectures too.
@@ -1403,12 +1437,10 @@ static void uprobe_warn(struct task_struct *t, const char *msg)
 
 static void dup_xol_work(struct callback_head *work)
 {
-       kfree(work);
-
        if (current->flags & PF_EXITING)
                return;
 
-       if (!__create_xol_area(current->utask->vaddr))
+       if (!__create_xol_area(current->utask->dup_xol_addr))
                uprobe_warn(current, "dup xol area");
 }
 
@@ -1419,7 +1451,6 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags)
 {
        struct uprobe_task *utask = current->utask;
        struct mm_struct *mm = current->mm;
-       struct callback_head *work;
        struct xol_area *area;
 
        t->utask = NULL;
@@ -1441,14 +1472,9 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags)
        if (mm == t->mm)
                return;
 
-       /* TODO: move it into the union in uprobe_task */
-       work = kmalloc(sizeof(*work), GFP_KERNEL);
-       if (!work)
-               return uprobe_warn(t, "dup xol area");
-
-       t->utask->vaddr = area->vaddr;
-       init_task_work(work, dup_xol_work);
-       task_work_add(t, work, true);
+       t->utask->dup_xol_addr = area->vaddr;
+       init_task_work(&t->utask->dup_xol_work, dup_xol_work);
+       task_work_add(t, &t->utask->dup_xol_work, true);
 }
 
 /*
index 832cb28105bbb7900a6c1342a095179303734215..763faf037ec1cccb135b98d1deccf339f69cd2e5 100644 (file)
@@ -61,7 +61,7 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr)
 static inline int init_kernel_text(unsigned long addr)
 {
        if (addr >= (unsigned long)_sinittext &&
-           addr <= (unsigned long)_einittext)
+           addr < (unsigned long)_einittext)
                return 1;
        return 0;
 }
@@ -69,7 +69,7 @@ static inline int init_kernel_text(unsigned long addr)
 int core_kernel_text(unsigned long addr)
 {
        if (addr >= (unsigned long)_stext &&
-           addr <= (unsigned long)_etext)
+           addr < (unsigned long)_etext)
                return 1;
 
        if (system_state == SYSTEM_BOOTING &&
index f6d11fc67f722201f1048527e00564ccd381afc1..b3080823a24d08d875da78af3f05b8d6cd72456a 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))
@@ -1402,13 +1402,11 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                p->tgid = p->pid;
        }
 
-       p->pdeath_signal = 0;
-       p->exit_state = 0;
-
        p->nr_dirtied = 0;
        p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10);
        p->dirty_paused_when = 0;
 
+       p->pdeath_signal = 0;
        INIT_LIST_HEAD(&p->thread_group);
        p->task_works = NULL;
 
index 8807061ca004cc0a532460244e01a56391eb0469..9328b80eaf14c347bb188bee856154ea07b86168 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 a3bb14fbe5c61fec344a166f8b542571dab4704c..dc04c166c54d7bc8e86ab87bec4075a3cc492e12 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 3e59f951d42f9663bfe39f7a8fc25bdc98b8b149..481a13c43b1708d3501a7f686760573c4eab6a34 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 cb228bf217603d6e841c1028dd610598ae133bff..abcd6ca86cb76b56e5979613a1964c0db743b5d0 100644 (file)
@@ -50,7 +50,7 @@ static void resume_irqs(bool want_early)
                bool is_early = desc->action &&
                        desc->action->flags & IRQF_EARLY_RESUME;
 
-               if (is_early != want_early)
+               if (!is_early && want_early)
                        continue;
 
                raw_spin_lock_irqsave(&desc->lock, flags);
index 1162f1030f18f9326c23522417a3de87feb18ef4..3320b84cc60f79ac09501d6f4d55acc01b0cebe6 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 7b5f012bde9d73ff246652fe48d49e5b1aebc12c..a1d8cc63b56e55ddbb83741b1d20db58e5ab3b7c 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 2a74f307c5ec57b050ceeb8a21341f8a394841ef..490afc03627e52e34e51d69e0a15693b7716b17a 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
index 576ba756a32d9c80948c72e31700738bcfc5ee06..eb8a54783fa0f47bb9b51568ec9dfed3d51cc48d 100644 (file)
@@ -590,6 +590,7 @@ static int very_verbose(struct lock_class *class)
 /*
  * Is this the address of a static object:
  */
+#ifdef __KERNEL__
 static int static_obj(void *obj)
 {
        unsigned long start = (unsigned long) &_stext,
@@ -616,6 +617,7 @@ static int static_obj(void *obj)
         */
        return is_module_address(addr) || is_module_percpu_address(addr);
 }
+#endif
 
 /*
  * To make lock name printouts unique, we calculate a unique
@@ -4115,6 +4117,7 @@ void debug_check_no_locks_held(void)
 }
 EXPORT_SYMBOL_GPL(debug_check_no_locks_held);
 
+#ifdef __KERNEL__
 void debug_show_all_locks(void)
 {
        struct task_struct *g, *p;
@@ -4172,6 +4175,7 @@ retry:
                read_unlock(&tasklist_lock);
 }
 EXPORT_SYMBOL_GPL(debug_show_all_locks);
+#endif
 
 /*
  * Careful: only use this function if you are sure that
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 24f9247b7d0214d4b3755bb4396f14d9ef712753..915e123a430fbb0cb6ea9197b90ab6ec3684a191 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 af5ebd21d77bae35906044e90271b7578093afa2..f5a3b1e8ec5137395fba2b42165aeb972f0530d0 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 f2970bddc5ea6224b8c0357970a543c90ac11da0..be5b8fac4bd0de72aba1f91674a2d0eb7a296d31 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 07af2c95dcfeea37f855db214e8e3405274bddb6..2abd25d79cc87bfc171f491c595d500e161f2de3 100644 (file)
@@ -46,6 +46,7 @@ static int padata_index_to_cpu(struct parallel_data *pd, int cpu_index)
 
 static int padata_cpu_hash(struct parallel_data *pd)
 {
+       unsigned int seq_nr;
        int cpu_index;
 
        /*
@@ -53,10 +54,8 @@ static int padata_cpu_hash(struct parallel_data *pd)
         * seq_nr mod. number of cpus in use.
         */
 
-       spin_lock(&pd->seq_lock);
-       cpu_index =  pd->seq_nr % cpumask_weight(pd->cpumask.pcpu);
-       pd->seq_nr++;
-       spin_unlock(&pd->seq_lock);
+       seq_nr = atomic_inc_return(&pd->seq_nr);
+       cpu_index = seq_nr % cpumask_weight(pd->cpumask.pcpu);
 
        return padata_index_to_cpu(pd, cpu_index);
 }
@@ -429,7 +428,7 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
        padata_init_pqueues(pd);
        padata_init_squeues(pd);
        setup_timer(&pd->timer, padata_reorder_timer, (unsigned long)pd);
-       pd->seq_nr = 0;
+       atomic_set(&pd->seq_nr, -1);
        atomic_set(&pd->reorder_objects, 0);
        atomic_set(&pd->refcnt, 0);
        pd->pinst = pinst;
index c00b4ceb39e8b151aab790e6b498247ea53a4aa6..6d6300375090e7b2345353174d85ed09f6f35ce4 100644 (file)
@@ -33,7 +33,7 @@ static int pause_on_oops;
 static int pause_on_oops_flag;
 static DEFINE_SPINLOCK(pause_on_oops_lock);
 
-int panic_timeout;
+int panic_timeout = CONFIG_PANIC_TIMEOUT;
 EXPORT_SYMBOL_GPL(panic_timeout);
 
 ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
index 10c22cae83a035e43eb54a79272fd1e6ae2fc757..b38109e204aff8e83afb0e15484cdfec8e450575 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 24850270c8024948d60c0f827457b18d2de2d9a7..98d357584cd6bad7bdc87cbc63e5c296a8393b60 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 0c9a934cfec19d0859b57c2ca9eca86830401ab2..1254f312d02483f524319ee5ab9d2e1a13f48678 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 4c06ddfea7cd2beffbf428a952e1efada1d1fa57..dd081987a8ec62349ad7721476cb97606077f28a 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 e85cda20ab2b8ed6694d1cfa4a617b1bf231569d..87c3bc47d99d80a214af07bfa84f7cf7b15e6e8e 100644 (file)
@@ -2003,6 +2003,9 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
        if (unlikely(prev_state == TASK_DEAD)) {
                task_numa_free(prev);
 
+               if (prev->sched_class->task_dead)
+                       prev->sched_class->task_dead(prev);
+
                /*
                 * Remove function-return probe instances associated with this
                 * task and put them back on the free list.
@@ -2414,10 +2417,10 @@ static inline void schedule_debug(struct task_struct *prev)
 {
        /*
         * Test if we are atomic. Since do_exit() needs to call into
-        * schedule() atomically, we ignore that path for now.
-        * Otherwise, whine if we are scheduling when we should not be.
+        * schedule() atomically, we ignore that path. Otherwise whine
+        * if we are scheduling when we should not.
         */
-       if (unlikely(in_atomic_preempt_off() && !prev->exit_state))
+       if (unlikely(in_atomic_preempt_off() && prev->state != TASK_DEAD))
                __schedule_bug(prev);
        rcu_sleep_check();
 
@@ -3653,7 +3656,7 @@ again:
        }
 
        double_rq_lock(rq, p_rq);
-       while (task_rq(p) != p_rq) {
+       if (task_rq(p) != p_rq) {
                double_rq_unlock(rq, p_rq);
                goto again;
        }
index fd773ade1a3141cd4b152cb8fce905866a7c223a..0de52f4f7717ad71d863ed82ecdba61c13e77766 100644 (file)
@@ -4110,12 +4110,16 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync)
  */
 static struct sched_group *
 find_idlest_group(struct sched_domain *sd, struct task_struct *p,
-                 int this_cpu, int load_idx)
+                 int this_cpu, int sd_flag)
 {
        struct sched_group *idlest = NULL, *group = sd->groups;
        unsigned long min_load = ULONG_MAX, this_load = 0;
+       int load_idx = sd->forkexec_idx;
        int imbalance = 100 + (sd->imbalance_pct-100)/2;
 
+       if (sd_flag & SD_BALANCE_WAKE)
+               load_idx = sd->wake_idx;
+
        do {
                unsigned long load, avg_load;
                int local_group;
@@ -4283,7 +4287,6 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
        }
 
        while (sd) {
-               int load_idx = sd->forkexec_idx;
                struct sched_group *group;
                int weight;
 
@@ -4292,10 +4295,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
                        continue;
                }
 
-               if (sd_flag & SD_BALANCE_WAKE)
-                       load_idx = sd->wake_idx;
-
-               group = find_idlest_group(sd, p, cpu, load_idx);
+               group = find_idlest_group(sd, p, cpu, sd_flag);
                if (!group) {
                        sd = sd->child;
                        continue;
@@ -5521,7 +5521,6 @@ static inline void update_sg_lb_stats(struct lb_env *env,
                        struct sched_group *group, int load_idx,
                        int local_group, struct sg_lb_stats *sgs)
 {
-       unsigned long nr_running;
        unsigned long load;
        int i;
 
@@ -5530,8 +5529,6 @@ static inline void update_sg_lb_stats(struct lb_env *env,
        for_each_cpu_and(i, sched_group_cpus(group), env->cpus) {
                struct rq *rq = cpu_rq(i);
 
-               nr_running = rq->nr_running;
-
                /* Bias balancing toward cpus of our domain */
                if (local_group)
                        load = target_load(i, load_idx);
@@ -5539,7 +5536,7 @@ static inline void update_sg_lb_stats(struct lb_env *env,
                        load = source_load(i, load_idx);
 
                sgs->group_load += load;
-               sgs->sum_nr_running += nr_running;
+               sgs->sum_nr_running += rq->nr_running;
 #ifdef CONFIG_NUMA_BALANCING
                sgs->nr_numa_running += rq->nr_numa_running;
                sgs->nr_preferred_running += rq->nr_preferred_running;
index 88c85b21d63346f4ba82e00962486445c9fdb410..b3b4a4953efcdae925c77e44fcf2798c69edd040 100644 (file)
@@ -1023,6 +1023,7 @@ struct sched_class {
        void (*set_curr_task) (struct rq *rq);
        void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);
        void (*task_fork) (struct task_struct *p);
+       void (*task_dead) (struct task_struct *p);
 
        void (*switched_from) (struct rq *this_rq, struct task_struct *task);
        void (*switched_to) (struct rq *this_rq, struct task_struct *task);
index 46116100f0ee2e9a74e45afff349d3420515df7a..bd9f940288388f6986ce5c8b7f4311ca7471d287 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 b24988353458c8cad2fff6aed7853f8625f1cf36..9a4500e4c1893ec22e17c4ac98a1d34cc6a0b6cf 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>
@@ -213,14 +211,48 @@ EXPORT_SYMBOL(local_bh_enable_ip);
 #define MAX_SOFTIRQ_TIME  msecs_to_jiffies(2)
 #define MAX_SOFTIRQ_RESTART 10
 
+#ifdef CONFIG_TRACE_IRQFLAGS
+/*
+ * When we run softirqs from irq_exit() and thus on the hardirq stack we need
+ * to keep the lockdep irq context tracking as tight as possible in order to
+ * not miss-qualify lock contexts and miss possible deadlocks.
+ */
+
+static inline bool lockdep_softirq_start(void)
+{
+       bool in_hardirq = false;
+
+       if (trace_hardirq_context(current)) {
+               in_hardirq = true;
+               trace_hardirq_exit();
+       }
+
+       lockdep_softirq_enter();
+
+       return in_hardirq;
+}
+
+static inline void lockdep_softirq_end(bool in_hardirq)
+{
+       lockdep_softirq_exit();
+
+       if (in_hardirq)
+               trace_hardirq_enter();
+}
+#else
+static inline bool lockdep_softirq_start(void) { return false; }
+static inline void lockdep_softirq_end(bool in_hardirq) { }
+#endif
+
 asmlinkage void __do_softirq(void)
 {
-       struct softirq_action *h;
-       __u32 pending;
        unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
-       int cpu;
        unsigned long old_flags = current->flags;
        int max_restart = MAX_SOFTIRQ_RESTART;
+       struct softirq_action *h;
+       bool in_hardirq;
+       __u32 pending;
+       int cpu;
 
        /*
         * Mask out PF_MEMALLOC s current task context is borrowed for the
@@ -233,7 +265,7 @@ asmlinkage void __do_softirq(void)
        account_irq_enter_time(current);
 
        __local_bh_disable(_RET_IP_, SOFTIRQ_OFFSET);
-       lockdep_softirq_enter();
+       in_hardirq = lockdep_softirq_start();
 
        cpu = smp_processor_id();
 restart:
@@ -280,16 +312,13 @@ restart:
                wakeup_softirqd();
        }
 
-       lockdep_softirq_exit();
-
+       lockdep_softirq_end(in_hardirq);
        account_irq_exit_time(current);
        __local_bh_enable(SOFTIRQ_OFFSET);
        WARN_ON_ONCE(in_interrupt());
        tsk_restore_flags(current, old_flags, PF_MEMALLOC);
 }
 
-
-
 asmlinkage void do_softirq(void)
 {
        __u32 pending;
@@ -377,13 +406,13 @@ void irq_exit(void)
 #endif
 
        account_irq_exit_time(current);
-       trace_hardirq_exit();
        preempt_count_sub(HARDIRQ_OFFSET);
        if (!in_interrupt() && local_softirq_pending())
                invoke_softirq();
 
        tick_irq_exit();
        rcu_irq_exit();
+       trace_hardirq_exit(); /* must be last! */
 }
 
 /*
@@ -627,146 +656,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 9f4618eb51c8528fe808d91f6dfdc6237af3ab65..13d2f7cd65dbfd851eaa61cf08645994c9fd0037 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 03cf44ac54d3666b434a26a30bc6f7a1e34cbf56..0e9f9eaade2f6a2dd0e729cd2d3bb38b4f6f8ec0 100644 (file)
@@ -367,9 +367,6 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list,
 
 static int __register_ftrace_function(struct ftrace_ops *ops)
 {
-       if (unlikely(ftrace_disabled))
-               return -ENODEV;
-
        if (FTRACE_WARN_ON(ops == &global_ops))
                return -EINVAL;
 
@@ -428,9 +425,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
 {
        int ret;
 
-       if (ftrace_disabled)
-               return -ENODEV;
-
        if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED)))
                return -EBUSY;
 
@@ -2088,10 +2082,15 @@ static void ftrace_startup_enable(int command)
 static int ftrace_startup(struct ftrace_ops *ops, int command)
 {
        bool hash_enable = true;
+       int ret;
 
        if (unlikely(ftrace_disabled))
                return -ENODEV;
 
+       ret = __register_ftrace_function(ops);
+       if (ret)
+               return ret;
+
        ftrace_start_up++;
        command |= FTRACE_UPDATE_CALLS;
 
@@ -2113,12 +2112,17 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)
        return 0;
 }
 
-static void ftrace_shutdown(struct ftrace_ops *ops, int command)
+static int ftrace_shutdown(struct ftrace_ops *ops, int command)
 {
        bool hash_disable = true;
+       int ret;
 
        if (unlikely(ftrace_disabled))
-               return;
+               return -ENODEV;
+
+       ret = __unregister_ftrace_function(ops);
+       if (ret)
+               return ret;
 
        ftrace_start_up--;
        /*
@@ -2153,9 +2157,10 @@ static void ftrace_shutdown(struct ftrace_ops *ops, int command)
        }
 
        if (!command || !ftrace_enabled)
-               return;
+               return 0;
 
        ftrace_run_update_code(command);
+       return 0;
 }
 
 static void ftrace_startup_sysctl(void)
@@ -3060,16 +3065,13 @@ static void __enable_ftrace_function_probe(void)
        if (i == FTRACE_FUNC_HASHSIZE)
                return;
 
-       ret = __register_ftrace_function(&trace_probe_ops);
-       if (!ret)
-               ret = ftrace_startup(&trace_probe_ops, 0);
+       ret = ftrace_startup(&trace_probe_ops, 0);
 
        ftrace_probe_registered = 1;
 }
 
 static void __disable_ftrace_function_probe(void)
 {
-       int ret;
        int i;
 
        if (!ftrace_probe_registered)
@@ -3082,9 +3084,7 @@ static void __disable_ftrace_function_probe(void)
        }
 
        /* no more funcs left */
-       ret = __unregister_ftrace_function(&trace_probe_ops);
-       if (!ret)
-               ftrace_shutdown(&trace_probe_ops, 0);
+       ftrace_shutdown(&trace_probe_ops, 0);
 
        ftrace_probe_registered = 0;
 }
@@ -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;
@@ -4290,12 +4366,15 @@ core_initcall(ftrace_nodyn_init);
 static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; }
 static inline void ftrace_startup_enable(int command) { }
 /* Keep as macros so we do not need to define the commands */
-# define ftrace_startup(ops, command)                  \
-       ({                                              \
-               (ops)->flags |= FTRACE_OPS_FL_ENABLED;  \
-               0;                                      \
+# define ftrace_startup(ops, command)                                  \
+       ({                                                              \
+               int ___ret = __register_ftrace_function(ops);           \
+               if (!___ret)                                            \
+                       (ops)->flags |= FTRACE_OPS_FL_ENABLED;          \
+               ___ret;                                                 \
        })
-# define ftrace_shutdown(ops, command) do { } while (0)
+# define ftrace_shutdown(ops, command) __unregister_ftrace_function(ops)
+
 # define ftrace_startup_sysctl()       do { } while (0)
 # define ftrace_shutdown_sysctl()      do { } while (0)
 
@@ -4320,12 +4399,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();
 }
@@ -4695,9 +4783,7 @@ int register_ftrace_function(struct ftrace_ops *ops)
 
        mutex_lock(&ftrace_lock);
 
-       ret = __register_ftrace_function(ops);
-       if (!ret)
-               ret = ftrace_startup(ops, 0);
+       ret = ftrace_startup(ops, 0);
 
        mutex_unlock(&ftrace_lock);
 
@@ -4716,9 +4802,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
        int ret;
 
        mutex_lock(&ftrace_lock);
-       ret = __unregister_ftrace_function(ops);
-       if (!ret)
-               ftrace_shutdown(ops, 0);
+       ret = ftrace_shutdown(ops, 0);
        mutex_unlock(&ftrace_lock);
 
        return ret;
@@ -4912,6 +4996,13 @@ ftrace_suspend_notifier_call(struct notifier_block *bl, unsigned long state,
        return NOTIFY_DONE;
 }
 
+/* Just a place holder for function graph */
+static struct ftrace_ops fgraph_ops __read_mostly = {
+       .func           = ftrace_stub,
+       .flags          = FTRACE_OPS_FL_STUB | FTRACE_OPS_FL_GLOBAL |
+                               FTRACE_OPS_FL_RECURSION_SAFE,
+};
+
 int register_ftrace_graph(trace_func_graph_ret_t retfunc,
                        trace_func_graph_ent_t entryfunc)
 {
@@ -4938,7 +5029,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,
        ftrace_graph_return = retfunc;
        ftrace_graph_entry = entryfunc;
 
-       ret = ftrace_startup(&global_ops, FTRACE_START_FUNC_RET);
+       ret = ftrace_startup(&fgraph_ops, FTRACE_START_FUNC_RET);
 
 out:
        mutex_unlock(&ftrace_lock);
@@ -4955,7 +5046,7 @@ void unregister_ftrace_graph(void)
        ftrace_graph_active--;
        ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
        ftrace_graph_entry = ftrace_graph_entry_stub;
-       ftrace_shutdown(&global_ops, FTRACE_STOP_FUNC_RET);
+       ftrace_shutdown(&fgraph_ops, FTRACE_STOP_FUNC_RET);
        unregister_pm_notifier(&ftrace_suspend_notifier);
        unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
 
index d9fea7dfd5d3bbb0570ac1e11537d3c1ce05d330..9d20cd9743efc0bf02cff0cf0f5536c19d0252cc 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;
@@ -1260,21 +1283,6 @@ int is_tracing_stopped(void)
        return global_trace.stop_count;
 }
 
-/**
- * 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
  *
@@ -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 73d08aa25b557a379a63c4602818c8cd33367547..ea189e027b80c3928a1c4390091b1eaa8f3699db 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 d594da0dc03ce53db530e238df1d410c4b89e29a..697fb9bac8f0d69d8e5a5023d5e5d10fdd3ed09c 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 78e27e3b52ac2ee0b9e86f544b77a45e12865d95..e854f420e033eb65a2bca233bb8df2e42778faf7 100644 (file)
@@ -24,6 +24,12 @@ static int   total_ref_count;
 static int perf_trace_event_perm(struct ftrace_event_call *tp_event,
                                 struct perf_event *p_event)
 {
+       if (tp_event->perf_perm) {
+               int ret = tp_event->perf_perm(tp_event, p_event);
+               if (ret)
+                       return ret;
+       }
+
        /* The ftrace function trace is allowed only for root. */
        if (ftrace_event_is_function(tp_event) &&
            perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN))
@@ -173,7 +179,7 @@ static int perf_trace_event_init(struct ftrace_event_call *tp_event,
 int perf_trace_init(struct perf_event *p_event)
 {
        struct ftrace_event_call *tp_event;
-       int event_id = p_event->attr.config;
+       u64 event_id = p_event->attr.config;
        int ret = -EINVAL;
 
        mutex_lock(&event_mutex);
index 368a4d50cc308cbe9564d4b01925b7937b0c4aba..f919a2e21bf30e68ad6a99ee627d63f5bb6cb44c 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 97daa8cf958df8e73c9fa8090f89cbaf4e63893e..2468f56dc5db2d1403df5692f1d9e0da912d6a1c 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 d21a74670088345fd97a81724e894ba9a0baff79..7c3e3e72e2b6bd2f0a6bd929ff67526497281b4a 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 b5c09242683d8cd1c989d2706702d4d42e182d73..0b99120d395cc166583e400e80108b7d07540935 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 243f6834d02675c9c1101e1eabe18e56eb7d3244..dae9541ada9ea9f46723878979f951834e8e84f1 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 b3dcfb2f0fef4e1bc9e9815021adc3e8dfe7d32a..0abd9b8634747e3001354f96b90dad379d6d2fd6 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 4e98e3b257a3a74d3716a0fc4ab35c2a1530fc43..3f34dc9b40f350bac7ae8869b17e4f5216b05d1a 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 847f88a6194b4e4183e0ed08dd03828f6e5ff1bd..7af67360b3307413865e2ed0719ea63894f01016 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 559329d9bd2fa4d9dd22f2ce31b4dcba6b5f1119..e4b6d11bdf78f35a2a6d8e831cd3ec21d97d9c00 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 272261b5f94ff84e3291be05aaa3b1a8c1ca00ca..b6dcc42ef7f5043e6115c53a5a47aa631bd04c57 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 630d72bf7e4173d2c47272ea7ce63012007ab192..509403e3fbc6b5bffbef9ecaa47cad6c68c03c3f 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 5bbb91988e69278f2cd012896db29688c9234476..a3a0dbfda32957616f143ae2722541a5846c0a62 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 13fb1134ba582e49c8aa3643feada72a2b0dae8b..240fb62cf3945aa0f7b601b343db65312a42f345 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 987293d03ebcf0e6bf1c6b81e8a4e68c7965e903..c66912be990fbda0f61e7a58fdbc52cf304566c2 100644 (file)
@@ -305,6 +305,9 @@ static DEFINE_HASHTABLE(unbound_pool_hash, UNBOUND_POOL_HASH_ORDER);
 /* I: attributes used when instantiating standard unbound pools on demand */
 static struct workqueue_attrs *unbound_std_wq_attrs[NR_STD_WORKER_POOLS];
 
+/* I: attributes used when instantiating ordered pools on demand */
+static struct workqueue_attrs *ordered_wq_attrs[NR_STD_WORKER_POOLS];
+
 struct workqueue_struct *system_wq __read_mostly;
 EXPORT_SYMBOL(system_wq);
 struct workqueue_struct *system_highpri_wq __read_mostly;
@@ -518,14 +521,21 @@ static inline void debug_work_activate(struct work_struct *work) { }
 static inline void debug_work_deactivate(struct work_struct *work) { }
 #endif
 
-/* allocate ID and assign it to @pool */
+/**
+ * worker_pool_assign_id - allocate ID and assing it to @pool
+ * @pool: the pool pointer of interest
+ *
+ * Returns 0 if ID in [0, WORK_OFFQ_POOL_NONE) is allocated and assigned
+ * successfully, -errno on failure.
+ */
 static int worker_pool_assign_id(struct worker_pool *pool)
 {
        int ret;
 
        lockdep_assert_held(&wq_pool_mutex);
 
-       ret = idr_alloc(&worker_pool_idr, pool, 0, 0, GFP_KERNEL);
+       ret = idr_alloc(&worker_pool_idr, pool, 0, WORK_OFFQ_POOL_NONE,
+                       GFP_KERNEL);
        if (ret >= 0) {
                pool->id = ret;
                return 0;
@@ -1320,7 +1330,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq,
 
        debug_work_activate(work);
 
-       /* if dying, only works from the same workqueue are allowed */
+       /* if draining, only works from the same workqueue are allowed */
        if (unlikely(wq->flags & __WQ_DRAINING) &&
            WARN_ON_ONCE(!is_chained_work(wq)))
                return;
@@ -1736,16 +1746,17 @@ static struct worker *create_worker(struct worker_pool *pool)
        if (IS_ERR(worker->task))
                goto fail;
 
+       set_user_nice(worker->task, pool->attrs->nice);
+
+       /* prevent userland from meddling with cpumask of workqueue workers */
+       worker->task->flags |= PF_NO_SETAFFINITY;
+
        /*
         * set_cpus_allowed_ptr() will fail if the cpumask doesn't have any
         * online CPUs.  It'll be re-applied when any of the CPUs come up.
         */
-       set_user_nice(worker->task, pool->attrs->nice);
        set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask);
 
-       /* prevent userland from meddling with cpumask of workqueue workers */
-       worker->task->flags |= PF_NO_SETAFFINITY;
-
        /*
         * The caller is responsible for ensuring %POOL_DISASSOCIATED
         * remains stable across this function.  See the comments above the
@@ -4106,7 +4117,7 @@ out_unlock:
 static int alloc_and_link_pwqs(struct workqueue_struct *wq)
 {
        bool highpri = wq->flags & WQ_HIGHPRI;
-       int cpu;
+       int cpu, ret;
 
        if (!(wq->flags & WQ_UNBOUND)) {
                wq->cpu_pwqs = alloc_percpu(struct pool_workqueue);
@@ -4126,6 +4137,13 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
                        mutex_unlock(&wq->mutex);
                }
                return 0;
+       } else if (wq->flags & __WQ_ORDERED) {
+               ret = apply_workqueue_attrs(wq, ordered_wq_attrs[highpri]);
+               /* there should only be single pwq for ordering guarantee */
+               WARN(!ret && (wq->pwqs.next != &wq->dfl_pwq->pwqs_node ||
+                             wq->pwqs.prev != &wq->dfl_pwq->pwqs_node),
+                    "ordering guarantee broken for workqueue %s\n", wq->name);
+               return ret;
        } else {
                return apply_workqueue_attrs(wq, unbound_std_wq_attrs[highpri]);
        }
@@ -5009,10 +5027,6 @@ static int __init init_workqueues(void)
        int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
        int i, cpu;
 
-       /* make sure we have enough bits for OFFQ pool ID */
-       BUILD_BUG_ON((1LU << (BITS_PER_LONG - WORK_OFFQ_POOL_SHIFT)) <
-                    WORK_CPU_END * NR_STD_WORKER_POOLS);
-
        WARN_ON(__alignof__(struct pool_workqueue) < __alignof__(long long));
 
        pwq_cache = KMEM_CACHE(pool_workqueue, SLAB_PANIC);
@@ -5051,13 +5065,23 @@ static int __init init_workqueues(void)
                }
        }
 
-       /* create default unbound wq attrs */
+       /* create default unbound and ordered wq attrs */
        for (i = 0; i < NR_STD_WORKER_POOLS; i++) {
                struct workqueue_attrs *attrs;
 
                BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL)));
                attrs->nice = std_nice[i];
                unbound_std_wq_attrs[i] = attrs;
+
+               /*
+                * An ordered wq should have only one pwq as ordering is
+                * guaranteed by max_active which is enforced by pwqs.
+                * Turn off NUMA so that dfl_pwq is used for all nodes.
+                */
+               BUG_ON(!(attrs = alloc_workqueue_attrs(GFP_KERNEL)));
+               attrs->nice = std_nice[i];
+               attrs->no_numa = true;
+               ordered_wq_attrs[i] = attrs;
        }
 
        system_wq = alloc_workqueue("events", 0, 0);
index 75485e163ca3bc8e807c10f7fdf2c761a7df377f..991c98bc4a3f51e9e7f377274084bec909483ea9 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 db25707aa41bc021b8174391091178b85258d466..6982094a7e74aa8c5daea87f90878174a8d746c1 100644 (file)
@@ -761,6 +761,15 @@ config PANIC_ON_OOPS_VALUE
        default 0 if !PANIC_ON_OOPS
        default 1 if PANIC_ON_OOPS
 
+config PANIC_TIMEOUT
+       int "panic timeout"
+       default 0
+       help
+         Set the timeout value (in seconds) until a reboot occurs when the
+         the kernel panics. If n = 0, then we wait forever. A timeout
+         value n > 0 will wait n seconds before rebooting, while a timeout
+         value n < 0 will reboot immediately.
+
 config SCHED_DEBUG
        bool "Collect scheduler debugging info"
        depends on DEBUG_KERNEL && PROC_FS
index d480a8c9238562b144e59217cdf4ecc05b574556..a459c31e8c6bb0286237b27bcb65d776bccad1b7 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 7b7f83027b7b748a4a1497d7804d4b54077d0790..d79b9d222065bd9467e4e091ad850a2ce4d93c94 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 4a70d120138cc684eb633ad93c8abedea826b20d..f76196d0740976001e964c444fe2fb13c9dcc3a4 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 af6e95d0bed6122bf9fd1e4af2bd76a4e2be62b3..f07a40d33871e2b9414ea40b53ee154108932d4e 100644 (file)
@@ -1,7 +1,8 @@
 #include <linux/export.h>
 #include <linux/lockref.h>
+#include <linux/mutex.h>
 
-#ifdef CONFIG_CMPXCHG_LOCKREF
+#if USE_CMPXCHG_LOCKREF
 
 /*
  * Allow weakly-ordered memory architectures to provide barrier-less
 # define cmpxchg64_relaxed cmpxchg64
 #endif
 
-/*
- * Allow architectures to override the default cpu_relax() within CMPXCHG_LOOP.
- * This is useful for architectures with an expensive cpu_relax().
- */
-#ifndef arch_mutex_cpu_relax
-# define arch_mutex_cpu_relax() cpu_relax()
-#endif
-
 /*
  * Note that the "cmpxchg()" reloads the "old" value for the
  * failure case.
index 657979f71bef0a0b3331eb804c0921d1f7afe6a8..bf076d281d4045da0b4b780ecd19a19db3987485 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 b0698ea972c65025976e87d6e1a5bb5beac92747..9d054bf91d0f3cd278494dedd3114dfd3b292bcb 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 82da4f4c3489eb0fa2a814adf477be16c14cbed4..1e5b2df442916de82ef497f844c068724f8cd555 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 4e8686c7e5a4085753121337755bf522ccfe6300..e4399fa65ad6b921aa88ed2a881d27118dda2dbb 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 48586ac3a62e3691c4950c1c72d904e6ebf036af..10909c571494893ffba0643784a115ab04267ac1 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 3f4ffda152bbe9e3ee8b5d2b5e667ce72345e12d..eb69f352401de910552fb7f28959b38f4746a878 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 ae4846ff48494e9ac5e733a5d5339b61763a49f5..b7749a92021c39b93907b94baf352f58f9db0359 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 0556c6a44959130e244127183acb9f16b7c3e604..bccd5a628ea6765478d2fa45dc01390a83ee8a5b 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 0b7656e804d126cf0fcfa04a8b427396af86deb1..dee6cf4e6d34135e1880c5c01c7627aa1a33c69a 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 e3cd40b2d5d926d45344bcdf2c513c2ee074c438..f1a0ae6e11b86b3020c90d7241ba12d47d2bbaa8 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 f9d78ec7831f0461549360575d9f779d206bac33..b7c171602ba1ebb8697f0c523f1a62f51c3a2fa4 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 bf8665849a5fed0b93f6022670c7c571e7eea8c6..5d9025f3b3e1cd65bd97655ee95d6cd2f390ce5b 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 4cc19f6ab6c6f84da6bb99955f5026a9bc066a76..eca4a3129129751208b41cfe808e9e31e5dc7b5f 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 dfc8300ecbb273fe3d7f5ef37e21e20a63b58c84..bb940045fe8595842ed58f2e32f87b83d40485e1 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);
 }
 
@@ -440,6 +441,54 @@ int migrate_huge_page_move_mapping(struct address_space *mapping,
        return MIGRATEPAGE_SUCCESS;
 }
 
+/*
+ * 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
  */
@@ -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 5a6baddde15d955e72e674e5bd468b9e95eceeb0..834b2d785f1e2f2fdce59a608f28a94b02b5d82d 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 6738c47f1f7280edc5f3fe610b2658195a0a77e0..1e4a600a6163645897a42defaf21f437fdf431a6 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 3929a40bd6c0a6d618d0dc0136fe6aecaad08628..cbb38545d9d6ab8d96ebcdb001ed9e19db772f59 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 fd3ee7a54a13a52c7d8761ff8e20508f352d083b..55c8b8dc9ffb0c349eb63ad20a8d7bbcc2e9b25d 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 0c8967bb201878e567c4d949ae1d9ee57cf04d0b..eb043bf05f4c57687c0644f11c12bd6b3a654795 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
  *
@@ -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 92737a0b787b57b0e5d8467879b50b4915a43f9e..545a170ebf9f66cf0e3716c9cd6f4cb7eef0eda6 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 7a9f80d451f548cd749331bc6143354eab53814d..84b26aaabd03b6d236ba82b84c713844916fd521 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 990afab2be1bcc1732167def7eb0336e38509588..9c5a1aa34d1253c725af29889d83759946af37c3 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 0715db64a5c3dd7ea0cd12f09c3fe62ee8385f3a..d334678c0bd8706d7c3efb6c0f35d0d62102c360 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 7fee50d637f956240a6146b342e7723a131ce4bd..7d424ac6e760bbb5d11ad3c164052374fd0491d1 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 737bef59ce899adc0a3e0ba67ea3d56536e5b7e5..7b491006eaf4000424282979b0c4709325102152 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 a00123ebb0ae0705c8e45e8433a07e96a2a06fe5..7bb1605bdfd999d134507b2a925b747bd1e5a07b 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 f6a1671ea2ff793bfca0efdd1541765803fb4d03..56ca494621c66e76465aaada6be27d5f6820e33e 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 71f0be1730801a615191a9badc2bd2f588bd4fbb..6a6c8bb4fd72d4f2d4294b9f7fed772d81e57b93 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 0cef677078381315c7ce3e58abb6573136bc227b..4af3821df880f70dd17cd36161eff766c3e49334 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 94d06cbfbc184a6e827aa5c3cbd15d4693eca49a..facd8a79c0383eb8898fca8dc905c87ed623202a 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 c4d3d423f89b84d41b9ff0e6919e9f37786f4960..3c2d3e4aa2f58a271bea6632451edd21b92668a9 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 12a0e51e21e13631beeec14d97e64aa137fd0e99..24fa3964b3c84da299287d6c913771c579d6a9a5 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 85a2796cac61bcc423ef724f1911f2a794667aff..4b07acb8293c3df3542e2a1661a6ae07bcfa6596 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 c41d5fbb91d0c3e07bf0eff148d57e6cd74ef8d0..4bf02adb5dc2433709ce06d6f4b4fdc48e171dd0 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 53f0990eab58e08a3da9160a27ee0834bd0f0272..af5ebd18d7059f2d0cc289e226c544f210309dc2 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 99c85668f5518ab9ee8d05a7b085139734893fdd..17fd5f2cb4b89cbf4e00b62b09bab7685b9f3965 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 05a41c7ec304e4256dca41b6b111bc1a267598b5..d6be3edb7a43493a18766c6824b0a1c9ac430d05 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 3ab8dd2e12828fea31d97b59dbeb3fe7521e953b..d249874a366d363edfc94adc47df54dde6bdba0e 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 89032580bd1d8aa5ef29d1395661bdd0bcfe95f1..618c6a8a911b65c9a406ba5d7d0e221cb124960e 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 8ffc52e01ece35db173fee05a3fdc160d733b684..ba3b7ea5ebb3139cca38e82ac6f5f5e346f4a4e0 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 5e78d44333b9bc39fdb56fdd489d0ac4f7f5d4e8..95897183226e18882bdbe16d8e9ba3a111ffae71 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 4cdb7c48dad6cb7ade5b1b9c76b32bff08c51d25..b61869429f4ced5a2ed08b178c20fc998bbfdaf2 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 8cec1e6b844df666b563429f538adf7695a527b6..2718fed53d8cf5b81a06c82f8cdf8ed96632185a 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 4e66bf61f585e58c2f975799833d540b5e5c9813..5325af85eea670f5c865367fc991eaf9b907cd98 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 426b5df1c98f1cc195158c9829a536cfbd0fefd6..459e200c08a40e4e737798582524cd973064b143 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 581a59504bd5f49d9a54584f3441c4e323b7bcd5..1865fdf5a5a5116be8bf28b25f9c340f8b8fd50b 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 aadec428e6ec206deb3c41c75a69f2c527bbab1d..cee4425b995689702375435f0be7dd85eca3ed73 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 7e49bbcc6967ebfaac72ee9e6b3e3b9032f1f88a..43f1b2bf469f40938a431582b305c45587049a04 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 b0bdd8c51e9c83a0ef968fc678588f3807559b13..ba5c1e002f37b2630c53a0284736f5d177bded76 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 22b1a7058fd3f841d94ee27655840c1ae325f011..d08c7a43dcd1c72f1512b01e8a00dd2b9e6b1869 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 b28e863fe0a7143b199bee51cfdf242c125f31cf..19e36376d2a083b5c8b307208ebd17ade086dcaa 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 ec9a9ef4ce50851639cf4cbe8e497390bea371f6..5afeb5aa4c7cfd9b0f794a45840f6fbd79b90315 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 caf01176a5e49774555b9c4dea5809e4f57cce19..90ff9570d7d4def935f3224514312fc217812d9c 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 5d9c845d288a3d8cce3eb96aeb65384e4a56edbc..52b802a0cd8cbda18579dd1811bce3405ecbdfdc 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 01cffeaa0085ede5bb802999fc1f29c9e6ec4b7f..f13bd91d9a56774f58dcf14de892b2bedd855cc5 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 9afbdb19f4a2f5dc1bccb6cf5d73db795fbf5f03..876c6ca2d8f9e77a28f1e629aa6523df1ee8f42f 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 41e1d2845c8f6690b5588888134bd80e49fc5dcf..5cb8ddb505ee8911461ec92a5c74feef0b441e00 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 f428935c50dba64b7cf7babd955978ed238fd928..f8da28278014ee2c99b2e530a694151d329bc5c2 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 8e8529d3c8c92a4473a3ed9fd8db876ca83533bd..c4638e6f023843bedbceb91f1bef3c424ad3b666 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 14bba8a1c5a74ed9d3e95e6de3bc2494b926a817..59a6f8b90cd9d7c9ca51bbcd2cc424153d6571df 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 2ab09cbae74d29155d08f7dd701851136d205e0f..06493736fbc826842876180a24510d9e348cb7f3 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 672854664ff5c1d36783ac1bfb72fea481648ca1..7820f3a7dd704bd6375c5cdc858e71b7be4261ed 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 89909dd730ddd65a4eac4f18e8a38925962b8534..5944d7d668dd91da21e945eac748bbbbbb11d67a 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 5658d9d51637beaa07790b49e4d405cc290afe16..12c97d8aa6bbb31ff1519459b89b7b9fb33817f5 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 ff75313f27a848db69dc0af6cf1a367205e13d2d..4fbdb7046d286cf64f60c61e3ae87ba657f042a0 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 df1fa58528c6fdeae476193f3bf7ae53d2e38a7e..d6062325db08411207fd63b435ec1e5e4e0f2001 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 f8a55ff1971b36db95ae7c22dd8c65062fbdcb61..3512177deb4d0e7d12c6d145781802074f490615 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 bf9f612c1bc24eb9eb0b26fbf7d0a62f74932735..f78f41aca8e90967a026a145f938746ce317cf10 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 3c00842b0079240f1788c83bfab346d5879336d8..e24ff1df0401288e4e810cf79ec3ea20d86a06c0 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 bfc6fcea38410e3a5817ad7ba254a98151bb0749..1b4a4a95367552c8cc22d19850b0779eee87cdc6 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 f3893e897f721aa4345c82f0ea2b899c44fa0ae4..81eb8cf8389b6a5af55f7b2994d7dbefe3d732bf 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 7a1e0fc1bd4dd2ca8f31d8a376d94a30406cb1e1..e096025b477f39c43ce65ddb8584193491bd62c1 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 0f676908d15b6108225817108d1cdddf0e31dc16..de7db23049f141be0b6f319db136e7d1a3c5a1f1 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 c32971269280116543c0bf560e1bbc3248df034d..a37b81fe04798e0c8cb0196f72b2cf97e4b610b7 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 168aff5e60de528194a1f4b8ea836cf394ea6613..c4b7218058b648856066bb24dacf38d54d16defc 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 911ef03bf8fbf1716672fb503f39de7fe13746d9..545f047868ad86c416f68f468a31d7cc5bd07eb4 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 571db8dd2292a7c5b1f2e940073edce370b13699..da1a1cee1a088e39816d565e99975bbc69a392d3 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 be446d517bc96641d413dc0bba09d9150398a81e..4cfd722e91536c1de137ec64f1e33bbc7fc5e1ea 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 ffda81ef1a709df605ef128ebc866cb00c75fe32..be5fadf3473946a3b7ef886eeadcb2d219e867aa 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 6cba486353e8a33dcba394e89664f3886da205c2..7b01b9f5846c845bcf4f04b21cfbcb5786c19421 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 48acec17e27a1524ced30bd86ad865f147ef3a3e..c3398cd99b94ed4c68da3927c1612da815f1a06d 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 1ded5c6d268c662af2e4743fedfb8226b5715cc3..35be035ee0cec79b5b797efb46c287661b8dbc93 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 e22d950c60b3c2a6e4ce7112ce13b9cb54e14d7a..43549eb7a7bec4312a1bbf768de2c125a79a526b 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 5f9bfd060deac302955f91dce75a3a86962a14d5..17c1bcb182c6b58a782744e23bfe671de160bf92 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 cdf4567ba9b330929aec53eb1c75d57a7047106e..9858e3e51a3a049ce796b3ed625e3d3ad8bbe5cc 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 a82667c64729a3964e8fbd44f0ddd69d1cefa5c3..da0c1f4ada128d79c41d0425d4072641b7b34fe3 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 e7c4e0e01ff5de40fb32cdeddd70cb204b84ea32..80c2e2d603e00c718ad1744804ef29f926b9d404 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 a1100640495d3d3b106e850e9af6a9cc4e1ed547..69345cebe3a3e397beffbe56536bf971fb81ddf9 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 dd1c37d7acbcfa6d126a8056360b8d6a56985035..8ef83ee97c6ad0fa934bbbe3d4988003e9ee287b 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 8f0897407a2cf950240082cfde9ab07969f96404..43817d73ccf997b69ec15da814e274404e08a2c7 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 8df7f64c6db35a0f538bef1fa0206f7826fd1a14..bca50b95c182300cbd89f8cfc21b17e9d6e31fe2 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 0c741cec4d0d294d808ddc26fdaacecc3e6a0b94..4518a57aa5febb14db1c1d750f217102dd8fd2a9 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;
 
@@ -432,52 +418,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 698814bfa7adfd4a58e9ec13f24eeb341ea86e08..53c19a35fc6dccd2e29318f123b537401c005314 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 d308402b67d80c192c948aa6db05c15089cfdbe8..824c6056bf823b0852b116af45698da916cdc772 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 84b7e3ea7b7ad7ce09e9256916589e4724d55969..a9b2342d5253295b607f3a3a1aa760e47c4d5ebe 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 cd958b381f9615911b1acfc6da6aceb39d373401..66bcd2eb577374bebb032dd32053a2e0835aed74 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 449e0776a2c0887bea75fbc963224a37d20ccb33..6f5e1dd3be2dbdcfdb591d64323c3558d0636e6d 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 d3d14a58aa91413bb18dae53bbb8aa1babd42c73..4067ea41be28d725c1312f2bb995df4e2c2209ab 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 5c2dab27610962caed49d6017a6cda5b5ac855fc..2c631fe76be191c1a7dd1bc33c1901c8507621b4 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 2e8286b47c28e5bef7cb80162e12ddb8ca4b358e..ac27c86ef6d11e00c2ecb1512b09bbda73c3eefc 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 c4e4b4561207354c49c9797dd5ea405867de0bcd..1035fa2d909c7f18c100266c85ec07bce4a73a97 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 12c30f3e643e00e3fa495cfc7d471e0078b00114..38946b26e471c9754c922d3451e8ec2682f5ae3d 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 77e38f733496c407f2b8a351fac733d3195a655a..008214a3d5eb5b90ecbccb3bea9f532a87f630c9 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 9f0f17cf6bf9b16e95c7a89d1bc452a0e4c0aada..de339b24ca140f5322a4c6d167450449f3f81bdc 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 e98fcfbe6007919d6c114708cde848a649bb2e4e..33af77246bfeb90c6b31bfe6163dc2c9cfbda787 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 4b48687c3890fc64c186b797181062a4cc9ac4fa..898492a8d61be8fde5bcdf66084d525bee23b5f0 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 fdc041c5785360731154521fb3492dba825776ff..95d843961907bf4b852743ea81015e7954db342b 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 c9b91cb1cb0dfdd4561ef3632d7443416a66b19d..68a27f9796d2ece54bcb53b98a47e8f98645077b 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 5ea573b37648b2591341b980c3ff1ab1632d202d..647396baa56f3670de372a6896f2b36477c17a32 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 c226aceee65b8b8c59d93d6a133a04c86177ceef..0b18693f2be6deb2f6f6b6bbf99aa131c767e830 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 6cd930f3678fb21384fc91e8927d0e49901af108..6c981ddc19f89a35115c823a1acf743eca61844d 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 1da52d1406fc87f0e625b0476bee3bedf682c6a1..42560e55d9789e946f5c02e73a0ed4b6c179409d 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 f1eb0d16666c2750b0001d923fabbd572f2cbe7d..458f85e9b0ba088575a72ef6dd6ff8d21484d290 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 f0f78c5f1c7d9690bc017db01ddf697d3022a81a..1ec19f6f0c2b9fe71ee9a7110873873274b9ab62 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 09fb638bcaa42ea88ff1bc7a6d34b6605f34a0c7..008cdade5aae387db601607c463aa20291777513 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 dab09dac8fc7ba08ed6ec1312eb840ef5d7bc5c0..f09b7db2c492f5a3f20a954e07e23b1a2f054c5e 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 d0d14a04dce1eb2e4274e3111d9008b71da918aa..bf04b30a788a5425b28ba70c11a50b77bfc9c697 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 b974571126fe90fce45ff6edc522366c463d5d8d..e7fbe368b4a38f665c538ae98b3c8db1ce2a5b81 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 17c88928b7db0abe001697867ead944bf6621712..dd9d295813cff2a8738946a3309fc31dcf57ebf0 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 cf465d66ccdec9506bab01cb7390fcd169b50dfc..69cd9bf3f561d64d377ddb65091b98e1017e15cf 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 8bcd4985d0fb341f795346f06c55d1f059c4c643..9f72a6376362e613cb87185acbb3581174c45592 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 3906527259d19f2d18d06d3641eda0b755d34968..3b61851bb9276ec733f7ab8c9d2179a5dd7fef4d 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 c1f403bed683ee8653356870f35457fe6e7f18eb..01625ccc3ae64ac3b5b1c664feec7f0136973e04 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 b5fa7e40cdcbce54fb0c1cb8cce5502e17b0f3c1..14810abedc2ec455adbde24094a5c468477e03d8 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 545c08b8a1d482ac0c155ab755f6623c62ffcbe5..5adfd94c5b85d3d48a6d48d3a4c7c2fa98526d8b 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 9d6986634e0bfaf1a4431cc8f6fc015f212e7042..687360da62d9f5e9a0075ba1a532da33ba973f98 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 0694d62e4dbc1b03f2b075269e23957c28ce85a2..c278b3356f75fe2de50b99b9e7e71609eb25a8df 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 7ceffe39d70e4bb69b046b2de1e604834412abb5..eb4580784d9dc5abdcaf5be27b24578ed6b0ca0e 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 7ab60babdd22a32bd44bd28afc80972e4311084c..403078d670a909cb20b4660e1a33965fd996bb52 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 aff8776e2d41a5d4020bdd5f8c937830e6e214a6..995c08c827b512bf4a9e6650f77ac834015e60fe 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 a6470ac39498e807a5d760e96b95bbc6b748129d..ec8b577db1354a6e6c33e631a3223f1fb5af7ccf 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 5dcd9c067bf0ac72a3c6e1067818053e31f7b748..b445b82020a83143aee4e9e89815dc921fdb4759 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 a7f4e7902104907adf86c9fffcfc0ab956d36095..a1eb21073176115a587f9eb1edf5d36dba582484 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 45a3ab5612c13c8a583ecd0e866c4113a898a896..7622789d37501f10d29af54a2c778daa85d960bc 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 cfe40addda764f9a1353cb29eb6a11b551a1eec9..2fca916d9edfd7a5a70a6e7e0a5df9fe2766d3a9 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 06473791c08adb7c5b0a7080ea9600d927c09d94..aa243db93f01fb88e32ad6fdd990f11034a4b18f 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 6f8e79e76c9e43aa6d181698c281e5b83b80a529..8dc3c2e7105a0474b5638c208a4acfc63c36a93e 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 8dcdca27d8360107f83c2fdfc70087f9464ccedd..69f0a1417e9a47669f5568af2097f031c247e607 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 db0e5cd34c70866e097e0af91d745d1d96d579fe..91c4117637ae1fdf33d385ea5c3f9eba601e03a8 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 6129020c41a94570ff49f5e42b5963a23105ba8e..549d0ab8c66204ec8db65de3d48ce619f7dee925 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 61090e0ff613aefeb5429c26d4efc7775ad97739..9c98100303774cd228ad83f3ff7bfcaeeb7af6fd 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 9a11f9f799f499f2d34d1dea3dec48feb36db00f..10085de886fef49b78a12746a2e0a593545d56e0 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 df198a5f482217781e7d4a605962bf443881ce7b..ba663e1dc7e35b7bf732c4863c1202e3249ac636 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 2c3963165a0d83f45b2d180361e12e4542588302..59184bb41ef81054384bcd641097f95e9e5426a2 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 c1d53200c306dc6202cbcc13555a05333c34411a..db1512ae30cc48d6f2ea87bc26a082a084551811 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 1500c38f0ccabf5908f7704f683759b0e754ebce..9d3b04b0769cbef51bfdcfcc75ac60cc2e1b67d1 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 3715b3e7212c9adccdc25483e632487a64360b54..bde0c6b6f9e87dbeff3004413123f6d2cef0d42f 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 c9a6775565bfa13252138f81ef8a0312b11cfea8..7caabdb51c647e12e35b500bdebd262ba1545eb0 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 6555a475453b882d2970c7ae4b16b5c4eb3286cf..1a9f53e535ca8ba87adc9b1af2bc761f128e56a0 100644 (file)
@@ -68,7 +68,6 @@ static void alloc_string(const char *str, int size)
 }
 %}
 
-ws     [ \n\t]
 n      [A-Za-z0-9_]
 
 %%
index dbd3e1ebbdada403bb5074669494428791ea2ba3..da058da413e7ec815dd3fdf891b26a75e3f7a8dd 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 bfcea5d3b27d35d64a57990bba8a6be3d9f0feed..17855761e6b77c4c5c13600b76c8cf95fcb82c00 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 9dfcd6d988dacaacebef7b91cd51a024618bfb00..deb2994b04c4952d48300e8c4c38a43cf462828b 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 a674fd5507c19db162af66787dcea6c65c19082d..d0da66396f6201b5dbce7e8b9aa2ae421aefd40a 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 17df3051747a93ee68a75255764408c813f1a14e..e25732b5d701127d904da3b608b825a520fce3ca 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 74f02e4dddd29eca8894fe409984ae525d1e2e96..58c4559290915594e1bd18a67770d0d4bafdc6e3 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 c26c81e925712fbc2ba38264f477bdd0a02f548d..a5918e01a4f71a6e97abc664682bd23f75bc544f 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 031d2d9dd6950b7e6c1bf6f3b16f2a37a9ab9e22..89c78658031f10bfc0527030ed970aebe3ff5471 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 84d1f5f538778b58f0b60c48d4a55ede44ff4c4f..1101c6f64bb7cb36602957ef2bcbc1538ee8aa63 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 26c607c971f5656da192698602dbdb85afcfa8f1..452567d3a08e7ccfc5a7e3ae20ae95307554f85c 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 30e8d7687259aaef15defab3883e8e1e52d91c1f..ba3dfd17f23f2671b20512a63c06ba75928ed0fc 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 2e7c9d6a2f3bb3f7b7ab6f4aab3b63a46a56a10a..fc3fa381d8506c5dc249e93809b15292c87ffb84 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 aeda0fbc8b2fe2273278edf84903dfe0a1b13925..288ca76e2fb116a6ccb1c366220ef1d918f7b67c 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 c51d2266587e8b62d09394c0d8320a4b53001569..777ac1c47253ef4f88aa1bc97c0539a9e0b96e83 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 fb99e18123b41b4f049fd98078e88aafccb7729b..4257b7e2796bf16e41db9ddca0e1f7c8e0d08a02 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 dbeb9bc27b24a14b7f546a44843bba2757db77cb..8b4f24ae43381de05af67271edd9a8ddd57c651f 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 0b759e17a1311abc3a2fb1f6fbf7e554b8f71ce5..b4af4ebc5be284d7f2665a5266be6a5bf267a683 100644 (file)
@@ -28,7 +28,7 @@ static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
 };
 
 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;
@@ -44,9 +44,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,
index b4754667659da1d6d8896a07f689c4b3f53fc2c8..9eae4809006be6f364ed9e5fe31ccb897381b856 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.
  */
index af9b6852f4e1bf571b55a2010fd6ab0488119cda..336b3ddfe63f5304374f860419cd3f5251bde5b8 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 b1753e98bf9aff919ab2b832de59e0858ae37abb..46408b9e62e876e4f711a5231ab0584aa5c7c1fd 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 74522dbd10a6e093fe293786f83a10ca8d7361bc..c49d3f14cbec96b49e2b8bedf15b4be570c5a7ef 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 39196abaff0d69d7d600ecd53847ba62b8cebed1..81a27971d884215bc20e71d756f6778893ae1d67 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
index 56dfee7cbf61c6605adf103dbf91393b33fe9256..d79263d2fdbfd0098666f541db32552c784688ce 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 b3dd616560f72054e13ddeaedaa39017585c2e5c..9636e17c9f5d709ae735d60bccddd3d2bc481470 100644 (file)
@@ -26,7 +26,8 @@
 
 #include "../integrity.h"
 
-enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
+enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
+                    IMA_SHOW_ASCII };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 
 /* digest size for IMA, fits SHA1 or MD5 */
@@ -36,23 +37,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 +95,22 @@ 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,
+                             struct ima_template_desc *desc, 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 +133,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 +173,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 +212,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 1c03e8f1e0e125cc948854e033d689a2aab22303..80374842fe0bb4cfef8a7dec8144eeff5278d408 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,35 @@ 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],
+                                                  entry->template_desc,
+                                                  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 +115,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 +182,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 +243,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 +257,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 +276,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 +296,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 2d4becab8918053a6d3520dc32527a33d92a3717..734e9468aca01c9a3724a57136f6d8bf371951f3 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;
 
index a02e0791cf15c7add98bd922ebcc08cd3db0f725..fdf60def52e90c93799e7d6ff6298ab4dd68c6d3 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,90 @@ 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,
+                                        struct ima_template_desc *td,
+                                        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++) {
+               if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) {
+                       rc = crypto_shash_update(&desc.shash,
+                                               (const u8 *) &field_data[i].len,
+                                               sizeof(field_data[i].len));
+                       if (rc)
+                               break;
+               }
+               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,
+                             struct ima_template_desc *desc, 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, desc, num_fields,
+                                          hash, tfm);
+
+       ima_free_tfm(tfm);
+
+       return rc;
 }
 
 static void __init ima_pcrread(int idx, u8 *pcr)
@@ -120,16 +211,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 +232,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 38477c9c3415cd9af47ec402e9694056a9ca3c22..db01125926bdb1e696165389bcdd26117e9d25fc 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,8 @@ 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;
+       bool is_ima_template = false;
+       int i;
 
        /* get entry */
        e = qe->entry;
@@ -134,18 +136,32 @@ 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)
+               is_ima_template = true;
+
+       if (!is_ima_template)
+               ima_putc(m, &e->template_data_len,
+                        sizeof(e->template_data_len));
+
+       /* 6th:  template specific data */
+       for (i = 0; i < e->template_desc->num_fields; i++) {
+               enum ima_show_type show = IMA_SHOW_BINARY;
+               struct ima_template_field *field = e->template_desc->fields[i];
 
-       /* 5th:  template specific data */
-       ima_template_show(m, (struct ima_template_data *)&e->template,
-                         IMA_SHOW_BINARY);
+               if (is_ima_template && strcmp(field->field_id, "d") == 0)
+                       show = IMA_SHOW_BINARY_NO_FIELD_LEN;
+               field->field_show(m, show, &e->template_data[i]);
+       }
        return 0;
 }
 
@@ -168,41 +184,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 +209,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 162ea723db3df5f07a2dd23f4bcc66e1af03a898..15f34bd40abed1530216be53c59658b6245d280b 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 e9508d5bbfcff7ee087d069697c6088da7da0210..149ee1119f87ba37c7673efdd7fd4d0e50cae809 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 399433ad614e0d26cc05588014991632a80865b0..a9c3d3cd1990d506a431614ffcf1d63ee53434a3 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 ff63fe00c19554921b172425d5261495a98a6bcf..d85e99761f4fc66afa251bb2bd657062d84d9b77 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..913e192
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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(const 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(const char *template_fmt,
+                                    struct ima_template_field ***fields,
+                                    int *num_fields)
+{
+       char *c, *template_fmt_copy;
+       int template_num_fields = template_fmt_size(template_fmt);
+       int i, result = 0;
+
+       if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX)
+               return -EINVAL;
+
+       /* copying is needed as strsep() modifies the original buffer */
+       template_fmt_copy = kstrdup(template_fmt, GFP_KERNEL);
+       if (template_fmt_copy == NULL)
+               return -ENOMEM;
+
+       *fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL);
+       if (*fields == NULL) {
+               result = -ENOMEM;
+               goto out;
+       }
+       for (i = 0; (c = strsep(&template_fmt_copy, "|")) != 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;
+out:
+       if (result < 0) {
+               kfree(*fields);
+               *fields = NULL;
+       }
+       kfree(template_fmt_copy);
+       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..c38adcc
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * 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)
+{
+       if (show != IMA_SHOW_BINARY_NO_FIELD_LEN)
+               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:
+       case IMA_SHOW_BINARY_NO_FIELD_LEN:
+               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 c42fb7a70dee78dfdf1ebbc5f1d3a43fb6c6c233..2fb5e53e927f2bf5432a34af1251c89f359d90f7 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
 
index a90d6d300dbd3b0b5849cae74af644e576e6dc45..a4f3f8c48d6e3f0aa8c5d7d21f900f425d517c71 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 504aaa008388c1595716e40f0e62e5fcd44fba71..dfb3a7bededf548ac1eed24b094de858e7a07df6 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 d65fa7fa29ba1a53b1ef4fb6d76c7aeafb7da65a..bbd32c729dbb4e019d1461116b84c25107e35ab8 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 d67c97bb10256d5dc5a9b74b3b8aaa37022f96b1..d3222b6d7d5979460ff63940066433e414b22ec4 100644 (file)
@@ -130,50 +130,6 @@ void key_gc_keytype(struct key_type *ktype)
        kleave("");
 }
 
-/*
- * 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
  */
@@ -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 d4f1468b9b50f46cd7d739544902a77a3ff40384..80b2aac4f50ceda614d03c815f7638aa88a0c933 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 8fb7c7bd465769cb5dca49e6d6f1ad011c75de63..55d110f0acedc96d17bcc5f5903aaa743ed6cb32 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 33cfd27b4de29650ae6ad0e1eb45646714a00f27..cee72ce642221e816968cb81069407fe01edb138 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 6ece7f2e5707f45c2736ca4a05504c2dd391ea00..69f0cb7bab7e873f8d8997d71db7c42430f72ce6 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,14 +144,225 @@ 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 217b6855e815cb851153fa08646d2bf145cee579..88e9a466940f642af60f61b407888155ba057be5 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 42defae1e161632e93b13b8194af1a30a09f2492..0cf8a130a267ca58fbc5599787c93b9913cfc576 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 c411f9bb156b205751ae06983e85f547119a245f..381411941cc1abbc48a699b1c2c38042f1a05711 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 85730d5a5a59a05c852b3d22c586778b117589fa..7495a93b4b9024dad78d526d17feb7d07f93016f 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 *);
@@ -221,33 +222,27 @@ error_alloc:
        return ERR_PTR(ret);
 }
 
-/*
- * 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 ee32d181764ab876fa2c6b0470c4a65f937cd031..8c0af08760c809b2923d04c5cc3b114c75e27b27 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 55dc88939185812f70145427b96c991ed6d636e2..faa2caeb593f8524a059e79d58e09bf430a1f992 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 234bc2ab450c61b42b1db2b53f631ab72bc48a39..9a62045e6282467493567a52f546d1e8d269bcd6 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 4dc31f4f2700626cb951aed5e874f0ce18d8b064..15b6928592ef68aac565e3fc94daf4737b6adc54 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 c540795fb3f2647619cb4705281872e93592e21e..794c3ca49eac92998caa17be71a4bdc472c2e9c8 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 aa47bcabb5f65e728aadbaa39cdecfa55d20aa16..b1dfe104945078ead53647c247c46aa6134fac2e 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 8fd8e18ea34019c863d91ba88268b8c4018f3410..fe341ae370049b39ac2012d665a64dd4dc9af198 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 6713f04e30ba8810415f88f7ed6e78cb5685f6f4..0dec76c64cf53853d0eea6aac983db307c8636b8 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 da4b8b2332802c9624f2f7f49ea8d622f96e180a..6235d052338b2e63b838711ed09c7ba1b04c67c6 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 c5454c0477c346e4d814f5ff209feba86e5b86ad..03a72c32afd738ccad5c188bbe853202c32f53f6 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 855e464e92efb9916535957ed53a3c9df2c1a33f..332ac8a80cf5b62c77bff350f6a92698d76a8e0f 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 ff427733c2903cab275a05da0887478850e1e374..5122affe06a8840e193150d62bd9b2f996fe67fe 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 30f119b1d1ec36a95dc456c52b5b0ac1a6868514..820313a04d49bf4c4a8bc0f04ea01514ff184a64 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 922f8afa89dd5837e2617daaf793db0e40ad009e..712c8a7b8e8b879d3835b5ee3650b66baa46e106 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 40de8d3f208ecf95db162f4ae355d0d53ba99265..c85bc1ec040c0c58f93772004361cbcd04861575 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 03bed52a80526abfbda766a33859595cc1d8bfa5..e93648774137c601f5ec90ce14a03983655ce36d 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 c8adde3aff8fdbe93fb2f867e55f71b9879685a5..f6195ebde3c94eef0cdf1cf92933246069b25059 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 b4feecc3fe0110d10bbdc183c369a03ab8495a6c..ee470a0b5c27fdad95a59b258792b6182435b999 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 d030818862146732ebe30c8cc3f266d485ef0677..a91d205ec0c6094cc9a0fecb5d427d4d24b1ed9a 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 076b8e8a51abd50d833bd2bc6a262349ed902f0e..364cc64fce717be1e75d16fa91f4aeaadc55539f 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 b3b59b1e93d6e6b056789243b77f0b319e56c982..14293cd9b1e53b4a260e9258a5cad54c75d71204 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 8825375cc031709b3918cd073cd574708c3f0405..b0be893ad44d52bd0f062a1747199330c4d6940d 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 80f4b4a45725bddba4f094d2fefdd4701c07cb6b..160aa08e3cd5ecd775c819941a056d5c04e1a13e 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 61ab640e195f3cbf6ee30d574116c797eead5a99..9dc5806d23dd51134b90fb0f8c0966a0588ccd6c 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 d9af6387f37c63acc631f670117b274c97e63824..9d518ac73eea064a6b4d918d3dba072cb50102f5 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 b35fe7345c203c557d2baa5d0cbb778afac2dfb9..8658578eb584997ec867a864a4e31a124519f828 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 9d93f02c6285d7648ea72d99ac5286ab6a45d19f..5e1c7bc73b290b8dba62f6f598eb765ebf054a7f 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 f664bae3b9b057f1a0b04776c1d4329a6650ed4c..328bd29264ce555e048da881af2dcc7e60e289d6 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 d3226892ad6b44953fd4d980d65874ac40535768..9048777228e2f058430f10636a7d9de74233c550 100644 (file)
@@ -434,17 +434,14 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
                return;
        index = s->packet_index;
 
+       /* this module generate empty packet for 'no data' */
        syt = calculate_syt(s, cycle);
-       if (!(s->flags & CIP_BLOCKING)) {
+       if (!(s->flags & CIP_BLOCKING))
                data_blocks = calculate_data_blocks(s);
-       } else {
-               if (syt != 0xffff) {
-                       data_blocks = s->syt_interval;
-               } else {
-                       data_blocks = 0;
-                       syt = 0xffffff;
-               }
-       }
+       else if (syt != 0xffff)
+               data_blocks = s->syt_interval;
+       else
+               data_blocks = 0;
 
        buffer = s->buffer.packets[index].buffer;
        buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
index 839ebf812d79e47302e3c4c57b5f2f74695551b7..2746ecd291af057bf778379cecd7530e353d5fc2 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
+#include <sound/asound.h>
 #include "packets-buffer.h"
 
 /**
index 6feee6614193a88d894c8f3e4f4942074523e4e6..57bcd31fcc123c38cd293cab40b52310734698a2 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 81aeb934261a26d2f2fca18060f21517b4650b32..0a90bd6ae2325d296f12f8d01a730e6d98fb0606 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 a2f87f9488ee163e336350c3c1b7cdebb48dca24..e5db001363eee9376d23e79f8a94483f62cd42d1 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 8de66ccd7279d682d84f1718d8cd2f5788ac9e06..4cdd9ded4563247318795b3b5f259dd828ed54b3 100644 (file)
@@ -209,8 +209,9 @@ config SND_HDA_CODEC_CA0132
 
 config SND_HDA_CODEC_CA0132_DSP
        bool "Support new DSP code for CA0132 codec"
-       depends on SND_HDA_CODEC_CA0132 && FW_LOADER
+       depends on SND_HDA_CODEC_CA0132
        select SND_HDA_DSP_LOADER
+       select FW_LOADER
        help
          Say Y here to enable the DSP for Creative CA0132 for extended
          features like equalizer or echo cancellation.
index dd5403d40830bfa2d97e6012b767fff52d64fddf..69178c4f4113fad7fb3111b57d49247c8f2ebe5c 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);
@@ -4003,6 +4000,10 @@ static void hda_call_codec_resume(struct hda_codec *codec)
         * in the resume / power-save sequence
         */
        hda_keep_power_on(codec);
+       if (codec->pm_down_notified) {
+               codec->pm_down_notified = 0;
+               hda_call_pm_notify(codec->bus, true);
+       }
        hda_set_power_state(codec, AC_PWRST_D0);
        restore_shutup_pins(codec);
        hda_exec_init_verbs(codec);
index 77db69480c195cde15ee64dd13bb18b2f81aa2de..7aa9870040c102df5b02aa0a94778c8e14c36024 100644 (file)
@@ -698,7 +698,6 @@ struct hda_bus {
        unsigned int in_reset:1;        /* during reset operation */
        unsigned int power_keep_link_on:1; /* don't power off HDA link */
        unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
-       unsigned int avoid_link_reset:1; /* don't reset link at runtime PM */
 
        int primary_dig_out_type;       /* primary digital out PCM type */
 };
index 32d3e3855a6eb99f298a97c3168e604a74ceae77..79ca80f6c77ada6d182a86b9880f3ce9125726f5 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 276f6e759bacee18dbbe1d5c969575906e34dacf..c4671d00babd6772193f955237c48412a3239a5a 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);
@@ -2506,12 +2506,8 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
 
        for (i = 0; i < num_pins; i++) {
                hda_nid_t pin = pins[i];
-               if (pin == spec->hp_mic_pin) {
-                       int ret = create_hp_mic_jack_mode(codec, pin);
-                       if (ret < 0)
-                               return ret;
+               if (pin == spec->hp_mic_pin)
                        continue;
-               }
                if (get_out_jack_num_items(codec, pin) > 1) {
                        struct snd_kcontrol_new *knew;
                        char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
@@ -2764,7 +2760,7 @@ static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
                        val &= ~(AC_PINCTL_VREFEN | PIN_HP);
                        val |= get_vref_idx(vref_caps, idx) | PIN_IN;
                } else
-                       val = snd_hda_get_default_vref(codec, nid);
+                       val = snd_hda_get_default_vref(codec, nid) | PIN_IN;
        }
        snd_hda_set_pin_ctl_cache(codec, nid, val);
        call_hp_automute(codec, NULL);
@@ -2784,9 +2780,6 @@ static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
        struct hda_gen_spec *spec = codec->spec;
        struct snd_kcontrol_new *knew;
 
-       if (get_out_jack_num_items(codec, pin) <= 1 &&
-           get_in_jack_num_items(codec, pin) <= 1)
-               return 0; /* no need */
        knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
                                    &hp_mic_jack_mode_enum);
        if (!knew)
@@ -2815,6 +2808,42 @@ static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
        return 0;
 }
 
+/* return true if either a volume or a mute amp is found for the given
+ * aamix path; the amp has to be either in the mixer node or its direct leaf
+ */
+static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid,
+                                  hda_nid_t pin, unsigned int *mix_val,
+                                  unsigned int *mute_val)
+{
+       int idx, num_conns;
+       const hda_nid_t *list;
+       hda_nid_t nid;
+
+       idx = snd_hda_get_conn_index(codec, mix_nid, pin, true);
+       if (idx < 0)
+               return false;
+
+       *mix_val = *mute_val = 0;
+       if (nid_has_volume(codec, mix_nid, HDA_INPUT))
+               *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+       if (nid_has_mute(codec, mix_nid, HDA_INPUT))
+               *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+       if (*mix_val && *mute_val)
+               return true;
+
+       /* check leaf node */
+       num_conns = snd_hda_get_conn_list(codec, mix_nid, &list);
+       if (num_conns < idx)
+               return false;
+       nid = list[idx];
+       if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT))
+               *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+       if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT))
+               *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+
+       return *mix_val || *mute_val;
+}
+
 /* create input playback/capture controls for the given pin */
 static int new_analog_input(struct hda_codec *codec, int input_idx,
                            hda_nid_t pin, const char *ctlname, int ctlidx,
@@ -2822,12 +2851,11 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
 {
        struct hda_gen_spec *spec = codec->spec;
        struct nid_path *path;
-       unsigned int val;
+       unsigned int mix_val, mute_val;
        int err, idx;
 
-       if (!nid_has_volume(codec, mix_nid, HDA_INPUT) &&
-           !nid_has_mute(codec, mix_nid, HDA_INPUT))
-               return 0; /* no need for analog loopback */
+       if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val))
+               return 0;
 
        path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
        if (!path)
@@ -2836,20 +2864,18 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
        spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
 
        idx = path->idx[path->depth - 1];
-       if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
-               val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
-               err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val);
+       if (mix_val) {
+               err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val);
                if (err < 0)
                        return err;
-               path->ctls[NID_PATH_VOL_CTL] = val;
+               path->ctls[NID_PATH_VOL_CTL] = mix_val;
        }
 
-       if (nid_has_mute(codec, mix_nid, HDA_INPUT)) {
-               val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
-               err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val);
+       if (mute_val) {
+               err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val);
                if (err < 0)
                        return err;
-               path->ctls[NID_PATH_MUTE_CTL] = val;
+               path->ctls[NID_PATH_MUTE_CTL] = mute_val;
        }
 
        path->active = true;
@@ -4383,6 +4409,17 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
        if (err < 0)
                return err;
 
+       /* create "Headphone Mic Jack Mode" if no input selection is
+        * available (or user specifies add_jack_modes hint)
+        */
+       if (spec->hp_mic_pin &&
+           (spec->auto_mic || spec->input_mux.num_items == 1 ||
+            spec->add_jack_modes)) {
+               err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin);
+               if (err < 0)
+                       return err;
+       }
+
        if (spec->add_jack_modes) {
                if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
                        err = create_out_jack_modes(codec, cfg->line_outs,
index 7a09404579a73ac729ad437c358f38ac3b2b807b..c6d230193da6214700e23369df0d2355c301012a 100644 (file)
@@ -2994,8 +2994,7 @@ static int azx_runtime_suspend(struct device *dev)
                  STATESTS_INT_MASK);
 
        azx_stop_chip(chip);
-       if (!chip->bus->avoid_link_reset)
-               azx_enter_link_reset(chip);
+       azx_enter_link_reset(chip);
        azx_clear_irq_pending(chip);
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
                hda_display_power(false);
index 87d2e0335ae4c3288fb239562a68fec79bbaa5b9..1a83559f4cbd27e455a941adaddc6d0513d9dd81 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 072755c8289c0ee440c5366bf04d5e074fcd2538..fc492ac24caa917c9d3670a37f83cb1d3e0bca95 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 c205bb1747fdf6a7d367d6ef54d718331d671c0b..1f2717f817a0142f4ef17910ffd50dafe42e4c09 100644 (file)
@@ -3244,9 +3244,29 @@ enum {
 #if IS_ENABLED(CONFIG_THINKPAD_ACPI)
 
 #include <linux/thinkpad_acpi.h>
+#include <acpi/acpi.h>
 
 static int (*led_set_func)(int, bool);
 
+static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
+                                void **rv)
+{
+       bool *found = context;
+       *found = true;
+       return AE_OK;
+}
+
+static bool is_thinkpad(struct hda_codec *codec)
+{
+       bool found = false;
+       if (codec->subsystem_id >> 16 != 0x17aa)
+               return false;
+       if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
+               return true;
+       found = false;
+       return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
+}
+
 static void update_tpacpi_mute_led(void *private_data, int enabled)
 {
        struct hda_codec *codec = private_data;
@@ -3279,6 +3299,8 @@ static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec,
        bool removefunc = false;
 
        if (action == HDA_FIXUP_ACT_PROBE) {
+               if (!is_thinkpad(codec))
+                       return;
                if (!led_set_func)
                        led_set_func = symbol_request(tpacpi_led_set);
                if (!led_set_func) {
@@ -3494,6 +3516,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
+       SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
        SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
        SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
        {}
index e68792311bb22850eb44ae0be6659ede770a1d06..08407bed093e5a64d4b659f90bf236dead9ec002 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 24d924d563aa9753b20816db5dddab0f8441528e..c770bdba6531c8a0d3eaf2f64e2c38d6d8ad758c 100644 (file)
@@ -1512,6 +1512,7 @@ enum {
        ALC260_FIXUP_KN1,
        ALC260_FIXUP_FSC_S7020,
        ALC260_FIXUP_FSC_S7020_JWSE,
+       ALC260_FIXUP_VAIO_PINS,
 };
 
 static void alc260_gpio1_automute(struct hda_codec *codec)
@@ -1652,6 +1653,24 @@ static const struct hda_fixup alc260_fixups[] = {
                .chained = true,
                .chain_id = ALC260_FIXUP_FSC_S7020,
        },
+       [ALC260_FIXUP_VAIO_PINS] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       /* Pin configs are missing completely on some VAIOs */
+                       { 0x0f, 0x01211020 },
+                       { 0x10, 0x0001003f },
+                       { 0x11, 0x411111f0 },
+                       { 0x12, 0x01a15930 },
+                       { 0x13, 0x411111f0 },
+                       { 0x14, 0x411111f0 },
+                       { 0x15, 0x411111f0 },
+                       { 0x16, 0x411111f0 },
+                       { 0x17, 0x411111f0 },
+                       { 0x18, 0x411111f0 },
+                       { 0x19, 0x411111f0 },
+                       { }
+               }
+       },
 };
 
 static const struct snd_pci_quirk alc260_fixup_tbl[] = {
@@ -1660,6 +1679,8 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1),
        SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750),
        SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900),
+       SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS),
+       SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F),
        SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020),
        SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1),
        SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1),
@@ -1761,6 +1782,8 @@ enum {
        ALC889_FIXUP_IMAC91_VREF,
        ALC882_FIXUP_INV_DMIC,
        ALC882_FIXUP_NO_PRIMARY_HP,
+       ALC887_FIXUP_ASUS_BASS,
+       ALC887_FIXUP_BASS_CHMAP,
 };
 
 static void alc889_fixup_coef(struct hda_codec *codec,
@@ -1894,6 +1917,9 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
        }
 }
 
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
+                                const struct hda_fixup *fix, int action);
+
 static const struct hda_fixup alc882_fixups[] = {
        [ALC882_FIXUP_ABIT_AW9D_MAX] = {
                .type = HDA_FIXUP_PINS,
@@ -2084,6 +2110,19 @@ static const struct hda_fixup alc882_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc882_fixup_no_primary_hp,
        },
+       [ALC887_FIXUP_ASUS_BASS] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       {0x16, 0x99130130}, /* bass speaker */
+                       {}
+               },
+               .chained = true,
+               .chain_id = ALC887_FIXUP_BASS_CHMAP,
+       },
+       [ALC887_FIXUP_BASS_CHMAP] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_bass_chmap,
+       },
 };
 
 static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -2117,6 +2156,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V),
        SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
        SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
+       SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS),
        SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
        SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
        SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
@@ -3344,8 +3384,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:
@@ -3391,7 +3433,7 @@ static void alc_update_headset_mode_hook(struct hda_codec *codec,
 static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
        struct alc_spec *spec = codec->spec;
-       spec->current_headset_type = ALC_HEADSET_MODE_UNKNOWN;
+       spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
        snd_hda_gen_hp_automute(codec, jack);
 }
 
@@ -3650,9 +3692,29 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec,
 #if IS_ENABLED(CONFIG_THINKPAD_ACPI)
 
 #include <linux/thinkpad_acpi.h>
+#include <acpi/acpi.h>
 
 static int (*led_set_func)(int, bool);
 
+static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
+                                void **rv)
+{
+       bool *found = context;
+       *found = true;
+       return AE_OK;
+}
+
+static bool is_thinkpad(struct hda_codec *codec)
+{
+       bool found = false;
+       if (codec->subsystem_id >> 16 != 0x17aa)
+               return false;
+       if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
+               return true;
+       found = false;
+       return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
+}
+
 static void update_tpacpi_mute_led(void *private_data, int enabled)
 {
        if (led_set_func)
@@ -3678,6 +3740,8 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
        bool removefunc = false;
 
        if (action == HDA_FIXUP_ACT_PROBE) {
+               if (!is_thinkpad(codec))
+                       return;
                if (!led_set_func)
                        led_set_func = symbol_request(tpacpi_led_set);
                if (!led_set_func) {
@@ -3753,6 +3817,7 @@ enum {
        ALC271_FIXUP_HP_GATE_MIC_JACK,
        ALC269_FIXUP_ACER_AC700,
        ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
+       ALC269VB_FIXUP_ASUS_ZENBOOK,
        ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
        ALC269VB_FIXUP_ORDISSIMO_EVE2,
        ALC283_FIXUP_CHROME_BOOK,
@@ -3921,6 +3986,8 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
        },
        [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = {
                .type = HDA_FIXUP_PINS,
@@ -4025,6 +4092,14 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc269_fixup_limit_int_mic_boost,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+       },
+       [ALC269VB_FIXUP_ASUS_ZENBOOK] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc269_fixup_limit_int_mic_boost,
+               .chained = true,
+               .chain_id = ALC269VB_FIXUP_DMIC,
        },
        [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
                .type = HDA_FIXUP_FUNC,
@@ -4068,8 +4143,6 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC269_FIXUP_THINKPAD_ACPI] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_thinkpad_acpi,
-               .chained = true,
-               .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
        },
        [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
                .type = HDA_FIXUP_PINS,
@@ -4126,6 +4199,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS),
        SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -4141,8 +4215,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
        SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
-       SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
-       SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC),
+       SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
+       SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK),
        SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
        SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
@@ -4171,7 +4245,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
-       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
+       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
@@ -4179,6 +4253,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
        SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+       SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
        SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
 
 #if 0
@@ -4666,7 +4741,7 @@ static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = {
 };
 
 /* override the 2.1 chmap */
-static void alc662_fixup_bass_chmap(struct hda_codec *codec,
+static void alc_fixup_bass_chmap(struct hda_codec *codec,
                                    const struct hda_fixup *fix, int action)
 {
        if (action == HDA_FIXUP_ACT_BUILD) {
@@ -4696,6 +4771,8 @@ enum {
        ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
        ALC668_FIXUP_HEADSET_MODE,
        ALC662_FIXUP_BASS_CHMAP,
+       ALC662_FIXUP_BASS_1A,
+       ALC662_FIXUP_BASS_1A_CHMAP,
 };
 
 static const struct hda_fixup alc662_fixups[] = {
@@ -4872,10 +4949,23 @@ static const struct hda_fixup alc662_fixups[] = {
        },
        [ALC662_FIXUP_BASS_CHMAP] = {
                .type = HDA_FIXUP_FUNC,
-               .v.func = alc662_fixup_bass_chmap,
+               .v.func = alc_fixup_bass_chmap,
                .chained = true,
                .chain_id = ALC662_FIXUP_ASUS_MODE4
        },
+       [ALC662_FIXUP_BASS_1A] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       {0x1a, 0x80106111}, /* bass speaker */
+                       {}
+               },
+       },
+       [ALC662_FIXUP_BASS_1A_CHMAP] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_bass_chmap,
+               .chained = true,
+               .chain_id = ALC662_FIXUP_BASS_1A,
+       },
 };
 
 static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -4888,8 +4978,10 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
        SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+       SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A_CHMAP),
        SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_CHMAP),
        SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_CHMAP),
        SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
@@ -5052,6 +5144,7 @@ static int patch_alc662(struct hda_codec *codec)
                case 0x10ec0272:
                case 0x10ec0663:
                case 0x10ec0665:
+               case 0x10ec0668:
                        set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
                        break;
                case 0x10ec0273:
@@ -5109,6 +5202,7 @@ static int patch_alc680(struct hda_codec *codec)
  */
 static const struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
+       { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
        { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
        { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
        { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
index 69a549a8234507e9b4ee14576717d779d05ff6c1..088a5afbd1b94846462cc290b7ca0eceab5ffaa7 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
 };
 
@@ -2093,7 +2094,8 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
 
        if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
-               codec->bus->avoid_link_reset = 1;
+               /* resetting controller clears GPIO, so we need to keep on */
+               codec->bus->power_keep_link_on = 1;
        }
 }
 
@@ -2106,6 +2108,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 +2604,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 +2625,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 +2672,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 01aecc2b50738353ad8a96f618c01be631981240..0d1c27e911b8dde2cf1b69827e8743a31a67316f 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 9a174fc47d39b38b0bf3ddfbd18f8367136744c4..39d774839b3ec064758604fbac83d5bfed2755bc 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 21ae8d4fdbfb3a0242502197386577232048d509..1ad92cbf0b24dfba1f97ecad1a8eca1b56940fad 100644 (file)
@@ -126,8 +126,6 @@ struct ab8500_codec_drvdata_dbg {
 
 /* Private data for AB8500 device-driver */
 struct ab8500_codec_drvdata {
-       struct regmap *regmap;
-
        /* Sidetone */
        long *sid_fir_values;
        enum sid_state sid_status;
@@ -168,34 +166,48 @@ static inline const char *amic_type_str(enum amic_type type)
  */
 
 /* Read a register from the audio-bank of AB8500 */
-static int ab8500_codec_read_reg(void *context, unsigned int reg,
-                                unsigned int *value)
+static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec,
+                                       unsigned int reg)
 {
-       struct device *dev = context;
        int status;
+       unsigned int value = 0;
 
        u8 value8;
-       status = abx500_get_register_interruptible(dev, AB8500_AUDIO,
-                                                  reg, &value8);
-       *value = (unsigned int)value8;
+       status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO,
+                                               reg, &value8);
+       if (status < 0) {
+               dev_err(codec->dev,
+                       "%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n",
+                       __func__, (u8)AB8500_AUDIO, (u8)reg, status);
+       } else {
+               dev_dbg(codec->dev,
+                       "%s: Read 0x%02x from register 0x%02x:0x%02x\n",
+                       __func__, value8, (u8)AB8500_AUDIO, (u8)reg);
+               value = (unsigned int)value8;
+       }
 
-       return status;
+       return value;
 }
 
 /* Write to a register in the audio-bank of AB8500 */
-static int ab8500_codec_write_reg(void *context, unsigned int reg,
-                                 unsigned int value)
+static int ab8500_codec_write_reg(struct snd_soc_codec *codec,
+                               unsigned int reg, unsigned int value)
 {
-       struct device *dev = context;
+       int status;
 
-       return abx500_set_register_interruptible(dev, AB8500_AUDIO,
-                                                reg, value);
-}
+       status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO,
+                                               reg, value);
+       if (status < 0)
+               dev_err(codec->dev,
+                       "%s: ERROR: Register (%02x:%02x) write failed (%d).\n",
+                       __func__, (u8)AB8500_AUDIO, (u8)reg, status);
+       else
+               dev_dbg(codec->dev,
+                       "%s: Wrote 0x%02x into register %02x:%02x\n",
+                       __func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg);
 
-static const struct regmap_config ab8500_codec_regmap = {
-       .reg_read = ab8500_codec_read_reg,
-       .reg_write = ab8500_codec_write_reg,
-};
+       return status;
+}
 
 /*
  * Controls - DAPM
@@ -2473,13 +2485,9 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 
        dev_dbg(dev, "%s: Enter.\n", __func__);
 
-       snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
-
        /* Setup AB8500 according to board-settings */
        pdata = dev_get_platdata(dev->parent);
 
-       codec->control_data = drvdata->regmap;
-
        if (np) {
                if (!pdata)
                        pdata = devm_kzalloc(dev,
@@ -2557,6 +2565,9 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver ab8500_codec_driver = {
        .probe =                ab8500_codec_probe,
+       .read =                 ab8500_codec_read_reg,
+       .write =                ab8500_codec_write_reg,
+       .reg_word_size =        sizeof(u8),
        .controls =             ab8500_ctrls,
        .num_controls =         ARRAY_SIZE(ab8500_ctrls),
        .dapm_widgets =         ab8500_dapm_widgets,
@@ -2581,15 +2592,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
        drvdata->anc_status = ANC_UNCONFIGURED;
        dev_set_drvdata(&pdev->dev, drvdata);
 
-       drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
-                                          &ab8500_codec_regmap);
-       if (IS_ERR(drvdata->regmap)) {
-               status = PTR_ERR(drvdata->regmap);
-               dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
-                       __func__, status);
-               return status;
-       }
-
        dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
        status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
                                ab8500_codec_dai,
index f2e62e45f91288916a252486a4ac130c5d7dd022..19e9f222d09c0934cb184843b40ff0ad52b4a1ec 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 6f05b17d19657aebb15f5793ec3cb36b07c57422..fea991031be18a05eab28e4922b10a2e472b5ea9 100644 (file)
@@ -1528,6 +1528,8 @@ static void arizona_enable_fll(struct arizona_fll *fll,
        /* Clear any pending completions */
        try_wait_for_completion(&fll->ok);
 
+       regmap_update_bits(arizona->regmap, fll->base + 1,
+                          ARIZONA_FLL1_FREERUN, 0);
        regmap_update_bits(arizona->regmap, fll->base + 1,
                           ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
        if (use_sync)
@@ -1546,6 +1548,8 @@ static void arizona_disable_fll(struct arizona_fll *fll)
        struct arizona *arizona = fll->arizona;
        bool change;
 
+       regmap_update_bits(arizona->regmap, fll->base + 1,
+                          ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
        regmap_update_bits_check(arizona->regmap, fll->base + 1,
                                 ARIZONA_FLL1_ENA, 0, &change);
        regmap_update_bits(arizona->regmap, fll->base + 0x11,
index 1a9412d86d172a81f058294f331bbaa549dcf654..6fb8f00f4191a7ad890f387c40dc4711dbbfddab 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 8bbddc151aa842eed14cae97cdabdcbbd9d8e469..a08e8bf6d07cb7db76cf8075d2e61d2ec2464b18 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 8c91be5d67e3c51d87166c03c6b2855c4e22ad16..c3c7396a618115d06171bb87314b363cc8ac9f6d 100644 (file)
@@ -37,6 +37,47 @@ struct wm5110_priv {
        struct arizona_fll fll[2];
 };
 
+static const struct reg_default wm5110_sysclk_revd_patch[] = {
+       { 0x3093, 0x1001 },
+       { 0x30E3, 0x1301 },
+       { 0x3133, 0x1201 },
+       { 0x3183, 0x1501 },
+       { 0x31D3, 0x1401 },
+};
+
+static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+       struct regmap *regmap = codec->control_data;
+       const struct reg_default *patch = NULL;
+       int i, patch_size;
+
+       switch (arizona->rev) {
+       case 3:
+               patch = wm5110_sysclk_revd_patch;
+               patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch);
+               break;
+       default:
+               return 0;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (patch)
+                       for (i = 0; i < patch_size; i++)
+                               regmap_write(regmap, patch[i].reg,
+                                            patch[i].def);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
@@ -101,13 +142,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,
@@ -400,7 +441,7 @@ static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
 
 static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
-                   0, NULL, 0),
+                   0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
                    ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
index ac1ff9947a90dbcc8353e8f146a82cd571c5dba3..543c5c2631b61827bcebca8af1926ea02b5655e9 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 6ec3de3efa4fa2f75ab0cc73230ae7353d7e3e7b..1392bb3c92540366c9a28817891567bfcfd45601 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 fa64cd85204fff5a9615f9cabbb5ac519a164d9d..fb5d107f56034eebec7003878eceded711a51769 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 41740e4888202263e72bb3900d04cd418513dd0c..c75d43bb2e92de772a97865e98227176473135a4 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 361f94f09b11565239b3edba824053b9bbd94e70..61e48852b9e8bd21b26eec98cca4fc9ba0eea145 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 2acf987844e8089704a4bf6d0ccde5ece2cc39e3..350ba23a9893484edb3d0585518b7048d568e875 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 78c35b44fc0489a05f8b9de03a7848e9d52a2618..b3653d37f75f087cffd3880a5c22e06c693c78fe 100644 (file)
@@ -200,9 +200,8 @@ static void rsnd_dma_do_work(struct work_struct *work)
                        return;
                }
 
+               dma_async_issue_pending(dma->chan);
        }
-
-       dma_async_issue_pending(dma->chan);
 }
 
 int rsnd_dma_available(struct rsnd_dma *dma)
@@ -288,15 +287,13 @@ int rsnd_dai_connect(struct rsnd_dai *rdai,
                     struct rsnd_mod *mod,
                     struct rsnd_dai_stream *io)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
-
-       if (!mod) {
-               dev_err(dev, "NULL mod\n");
+       if (!mod)
                return -EIO;
-       }
 
        if (!list_empty(&mod->list)) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+
                dev_err(dev, "%s%d is not empty\n",
                        rsnd_mod_name(mod),
                        rsnd_mod_id(mod));
index f4453e33a847653bac7fd7387f23e44e879f3c63..fa8fa15860b9bda89dc5d3af73c85a5c4f076f15 100644 (file)
@@ -68,7 +68,7 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv,
                return 0;
 
        id = rsnd_mod_id(mod);
-       if (id < 0 || id > ARRAY_SIZE(routes))
+       if (id < 0 || id >= ARRAY_SIZE(routes))
                return -EIO;
 
        /*
index b47f6fe6277fd72aadfeadda3eddb623a3215291..dbb1b625eb2f59b5fd536fc33c0679b054095652 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 b9ba0fcc45df10d4151bb39f8a5d6284dc8ec23e..83aabea259d7113d82d94d870bd04ebffd64b633 100644 (file)
@@ -636,8 +636,22 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
        if (usb_pipein(ep->pipe) ||
                        snd_usb_endpoint_implicit_feedback_sink(ep)) {
 
+               urb_packs = packs_per_ms;
+               /*
+                * Wireless devices can poll at a max rate of once per 4ms.
+                * For dataintervals less than 5, increase the packet count to
+                * allow the host controller to use bursting to fill in the
+                * gaps.
+                */
+               if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) {
+                       int interval = ep->datainterval;
+                       while (interval < 5) {
+                               urb_packs <<= 1;
+                               ++interval;
+                       }
+               }
                /* make capture URBs <= 1 ms and smaller than a period */
-               urb_packs = min(max_packs_per_urb, packs_per_ms);
+               urb_packs = min(max_packs_per_urb, urb_packs);
                while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
                        urb_packs >>= 1;
                ep->nurbs = MAX_URBS;
index d42a584cf8292d99d349a309f55e2d1724e45898..3454262358b398913779954846ab12c11e7c5973 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 d737d0e6e5580efdec49852b93ed610429c88e31..2fb71be5e100b0973c0993dbb8ed4da3f35c5cf2 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;
diff --git a/tools/lib/lockdep/Makefile b/tools/lib/lockdep/Makefile
new file mode 100644 (file)
index 0000000..da8b7aa
--- /dev/null
@@ -0,0 +1,251 @@
+# liblockdep version
+LL_VERSION = 0
+LL_PATCHLEVEL = 0
+LL_EXTRAVERSION = 1
+
+# file format version
+FILE_VERSION = 1
+
+MAKEFLAGS += --no-print-directory
+
+
+# Makefiles suck: This macro sets a default value of $(2) for the
+# variable named by $(1), unless the variable has been set by
+# environment or command line. This is necessary for CC and AR
+# because make sets default values, so the simpler ?= approach
+# won't work as expected.
+define allow-override
+  $(if $(or $(findstring environment,$(origin $(1))),\
+            $(findstring command line,$(origin $(1)))),,\
+    $(eval $(1) = $(2)))
+endef
+
+# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,AR,$(CROSS_COMPILE)ar)
+
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+prefix ?= /usr/local
+libdir_relative = lib
+libdir = $(prefix)/$(libdir_relative)
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+
+export DESTDIR DESTDIR_SQ INSTALL
+
+# copy a bit from Linux kbuild
+
+ifeq ("$(origin V)", "command line")
+  VERBOSE = $(V)
+endif
+ifndef VERBOSE
+  VERBOSE = 0
+endif
+
+ifeq ("$(origin O)", "command line")
+  BUILD_OUTPUT := $(O)
+endif
+
+ifeq ($(BUILD_SRC),)
+ifneq ($(BUILD_OUTPUT),)
+
+define build_output
+       $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \
+       BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1
+endef
+
+saved-output := $(BUILD_OUTPUT)
+BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd)
+$(if $(BUILD_OUTPUT),, \
+     $(error output directory "$(saved-output)" does not exist))
+
+all: sub-make
+
+gui: force
+       $(call build_output, all_cmd)
+
+$(filter-out gui,$(MAKECMDGOALS)): sub-make
+
+sub-make: force
+       $(call build_output, $(MAKECMDGOALS))
+
+
+# Leave processing to above invocation of make
+skip-makefile := 1
+
+endif # BUILD_OUTPUT
+endif # BUILD_SRC
+
+# We process the rest of the Makefile if this is the final invocation of make
+ifeq ($(skip-makefile),)
+
+srctree                := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))
+objtree                := $(CURDIR)
+src            := $(srctree)
+obj            := $(objtree)
+
+export prefix libdir bindir src obj
+
+# Shell quotes
+libdir_SQ = $(subst ','\'',$(libdir))
+bindir_SQ = $(subst ','\'',$(bindir))
+
+LIB_FILE = liblockdep.a liblockdep.so
+BIN_FILE = lockdep
+
+CONFIG_INCLUDES =
+CONFIG_LIBS    =
+CONFIG_FLAGS   =
+
+OBJ            = $@
+N              =
+
+export Q VERBOSE
+
+LIBLOCKDEP_VERSION = $(LL_VERSION).$(LL_PATCHLEVEL).$(LL_EXTRAVERSION)
+
+INCLUDES = -I. -I/usr/local/include -I./uinclude $(CONFIG_INCLUDES)
+
+# Set compile option CFLAGS if not set elsewhere
+CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g
+
+override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
+
+ifeq ($(VERBOSE),1)
+  Q =
+  print_compile =
+  print_app_build =
+  print_fpic_compile =
+  print_shared_lib_compile =
+  print_install =
+else
+  Q = @
+  print_compile =              echo '  CC                 '$(OBJ);
+  print_app_build =            echo '  BUILD              '$(OBJ);
+  print_fpic_compile =         echo '  CC FPIC            '$(OBJ);
+  print_shared_lib_compile =   echo '  BUILD SHARED LIB   '$(OBJ);
+  print_static_lib_build =     echo '  BUILD STATIC LIB   '$(OBJ);
+  print_install =              echo '  INSTALL     '$1'        to      $(DESTDIR_SQ)$2';
+endif
+
+do_fpic_compile =                                      \
+       ($(print_fpic_compile)                          \
+       $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@)
+
+do_app_build =                                         \
+       ($(print_app_build)                             \
+       $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS))
+
+do_compile_shared_library =                    \
+       ($(print_shared_lib_compile)            \
+       $(CC) --shared $^ -o $@ -lpthread -ldl)
+
+do_build_static_lib =                          \
+       ($(print_static_lib_build)              \
+       $(RM) $@;  $(AR) rcs $@ $^)
+
+
+define do_compile
+       $(print_compile)                                                \
+       $(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
+endef
+
+$(obj)/%.o: $(src)/%.c
+       $(Q)$(call do_compile)
+
+%.o: $(src)/%.c
+       $(Q)$(call do_compile)
+
+PEVENT_LIB_OBJS = common.o lockdep.o preload.o rbtree.o
+
+ALL_OBJS = $(PEVENT_LIB_OBJS)
+
+CMD_TARGETS = $(LIB_FILE)
+
+TARGETS = $(CMD_TARGETS)
+
+
+all: all_cmd
+
+all_cmd: $(CMD_TARGETS)
+
+liblockdep.so: $(PEVENT_LIB_OBJS)
+       $(Q)$(do_compile_shared_library)
+
+liblockdep.a: $(PEVENT_LIB_OBJS)
+       $(Q)$(do_build_static_lib)
+
+$(PEVENT_LIB_OBJS): %.o: $(src)/%.c
+       $(Q)$(do_fpic_compile)
+
+## make deps
+
+all_objs := $(sort $(ALL_OBJS))
+all_deps := $(all_objs:%.o=.%.d)
+
+# let .d file also depends on the source and header files
+define check_deps
+               @set -e; $(RM) $@; \
+               $(CC) -MM $(CFLAGS) $< > $@.$$$$; \
+               sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+               $(RM) $@.$$$$
+endef
+
+$(all_deps): .%.d: $(src)/%.c
+       $(Q)$(call check_deps)
+
+$(all_objs) : %.o : .%.d
+
+dep_includes := $(wildcard $(all_deps))
+
+ifneq ($(dep_includes),)
+ include $(dep_includes)
+endif
+
+### Detect environment changes
+TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
+
+tags:  force
+       $(RM) tags
+       find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
+       --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
+
+TAGS:  force
+       $(RM) TAGS
+       find . -name '*.[ch]' | xargs etags \
+       --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
+
+define do_install
+       $(print_install)                                \
+       if [ ! -d '$(DESTDIR_SQ)$2' ]; then             \
+               $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+       fi;                                             \
+       $(INSTALL) $1 '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: all_cmd
+       $(Q)$(call do_install,$(LIB_FILE),$(libdir_SQ))
+       $(Q)$(call do_install,$(BIN_FILE),$(bindir_SQ))
+
+install: install_lib
+
+clean:
+       $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d
+       $(RM) tags TAGS
+
+endif # skip-makefile
+
+PHONY += force
+force:
+
+# Declare the contents of the .PHONY variable as phony.  We keep that
+# information in a variable so we can use it in if_changed and friends.
+.PHONY: $(PHONY)
diff --git a/tools/lib/lockdep/common.c b/tools/lib/lockdep/common.c
new file mode 100644 (file)
index 0000000..8ef602f
--- /dev/null
@@ -0,0 +1,33 @@
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/compiler.h>
+#include <linux/lockdep.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+static __thread struct task_struct current_obj;
+
+/* lockdep wants these */
+bool debug_locks = true;
+bool debug_locks_silent;
+
+__attribute__((constructor)) static void liblockdep_init(void)
+{
+       lockdep_init();
+}
+
+__attribute__((destructor)) static void liblockdep_exit(void)
+{
+       debug_check_no_locks_held(&current_obj);
+}
+
+struct task_struct *__curr(void)
+{
+       if (current_obj.pid == 0) {
+               /* Makes lockdep output pretty */
+               prctl(PR_GET_NAME, current_obj.comm);
+               current_obj.pid = syscall(__NR_gettid);
+       }
+
+       return &current_obj;
+}
diff --git a/tools/lib/lockdep/include/liblockdep/common.h b/tools/lib/lockdep/include/liblockdep/common.h
new file mode 100644 (file)
index 0000000..0bda630
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _LIBLOCKDEP_COMMON_H
+#define _LIBLOCKDEP_COMMON_H
+
+#include <pthread.h>
+
+#define NR_LOCKDEP_CACHING_CLASSES 2
+#define MAX_LOCKDEP_SUBCLASSES 8UL
+
+#ifndef CALLER_ADDR0
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+#endif
+
+#ifndef _RET_IP_
+#define _RET_IP_ CALLER_ADDR0
+#endif
+
+#ifndef _THIS_IP_
+#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })
+#endif
+
+struct lockdep_subclass_key {
+       char __one_byte;
+};
+
+struct lock_class_key {
+       struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES];
+};
+
+struct lockdep_map {
+       struct lock_class_key   *key;
+       struct lock_class       *class_cache[NR_LOCKDEP_CACHING_CLASSES];
+       const char              *name;
+#ifdef CONFIG_LOCK_STAT
+       int                     cpu;
+       unsigned long           ip;
+#endif
+};
+
+void lockdep_init_map(struct lockdep_map *lock, const char *name,
+                       struct lock_class_key *key, int subclass);
+void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
+                       int trylock, int read, int check,
+                       struct lockdep_map *nest_lock, unsigned long ip);
+void lock_release(struct lockdep_map *lock, int nested,
+                       unsigned long ip);
+
+#define STATIC_LOCKDEP_MAP_INIT(_name, _key) \
+       { .name = (_name), .key = (void *)(_key), }
+
+#endif
diff --git a/tools/lib/lockdep/include/liblockdep/mutex.h b/tools/lib/lockdep/include/liblockdep/mutex.h
new file mode 100644 (file)
index 0000000..c342f70
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef _LIBLOCKDEP_MUTEX_H
+#define _LIBLOCKDEP_MUTEX_H
+
+#include <pthread.h>
+#include "common.h"
+
+struct liblockdep_pthread_mutex {
+       pthread_mutex_t mutex;
+       struct lockdep_map dep_map;
+};
+
+typedef struct liblockdep_pthread_mutex liblockdep_pthread_mutex_t;
+
+#define LIBLOCKDEP_PTHREAD_MUTEX_INITIALIZER(mtx)                      \
+               (const struct liblockdep_pthread_mutex) {               \
+       .mutex = PTHREAD_MUTEX_INITIALIZER,                             \
+       .dep_map = STATIC_LOCKDEP_MAP_INIT(#mtx, &((&(mtx))->dep_map)), \
+}
+
+static inline int __mutex_init(liblockdep_pthread_mutex_t *lock,
+                               const char *name,
+                               struct lock_class_key *key,
+                               const pthread_mutexattr_t *__mutexattr)
+{
+       lockdep_init_map(&lock->dep_map, name, key, 0);
+       return pthread_mutex_init(&lock->mutex, __mutexattr);
+}
+
+#define liblockdep_pthread_mutex_init(mutex, mutexattr)                \
+({                                                             \
+       static struct lock_class_key __key;                     \
+                                                               \
+       __mutex_init((mutex), #mutex, &__key, (mutexattr));     \
+})
+
+static inline int liblockdep_pthread_mutex_lock(liblockdep_pthread_mutex_t *lock)
+{
+       lock_acquire(&lock->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+       return pthread_mutex_lock(&lock->mutex);
+}
+
+static inline int liblockdep_pthread_mutex_unlock(liblockdep_pthread_mutex_t *lock)
+{
+       lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
+       return pthread_mutex_unlock(&lock->mutex);
+}
+
+static inline int liblockdep_pthread_mutex_trylock(liblockdep_pthread_mutex_t *lock)
+{
+       lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+       return pthread_mutex_trylock(&lock->mutex) == 0 ? 1 : 0;
+}
+
+static inline int liblockdep_pthread_mutex_destroy(liblockdep_pthread_mutex_t *lock)
+{
+       return pthread_mutex_destroy(&lock->mutex);
+}
+
+#ifdef __USE_LIBLOCKDEP
+
+#define pthread_mutex_t         liblockdep_pthread_mutex_t
+#define pthread_mutex_init      liblockdep_pthread_mutex_init
+#define pthread_mutex_lock      liblockdep_pthread_mutex_lock
+#define pthread_mutex_unlock    liblockdep_pthread_mutex_unlock
+#define pthread_mutex_trylock   liblockdep_pthread_mutex_trylock
+#define pthread_mutex_destroy   liblockdep_pthread_mutex_destroy
+
+#endif
+
+#endif
diff --git a/tools/lib/lockdep/include/liblockdep/rwlock.h b/tools/lib/lockdep/include/liblockdep/rwlock.h
new file mode 100644 (file)
index 0000000..a680ab8
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef _LIBLOCKDEP_RWLOCK_H
+#define _LIBLOCKDEP_RWLOCK_H
+
+#include <pthread.h>
+#include "common.h"
+
+struct liblockdep_pthread_rwlock {
+       pthread_rwlock_t rwlock;
+       struct lockdep_map dep_map;
+};
+
+typedef struct liblockdep_pthread_rwlock liblockdep_pthread_rwlock_t;
+
+#define LIBLOCKDEP_PTHREAD_RWLOCK_INITIALIZER(rwl)                     \
+               (struct liblockdep_pthread_rwlock) {                    \
+       .rwlock = PTHREAD_RWLOCK_INITIALIZER,                           \
+       .dep_map = STATIC_LOCKDEP_MAP_INIT(#rwl, &((&(rwl))->dep_map)), \
+}
+
+static inline int __rwlock_init(liblockdep_pthread_rwlock_t *lock,
+                               const char *name,
+                               struct lock_class_key *key,
+                               const pthread_rwlockattr_t *attr)
+{
+       lockdep_init_map(&lock->dep_map, name, key, 0);
+
+       return pthread_rwlock_init(&lock->rwlock, attr);
+}
+
+#define liblockdep_pthread_rwlock_init(lock, attr)             \
+({                                                     \
+       static struct lock_class_key __key;             \
+                                                       \
+       __rwlock_init((lock), #lock, &__key, (attr));   \
+})
+
+static inline int liblockdep_pthread_rwlock_rdlock(liblockdep_pthread_rwlock_t *lock)
+{
+       lock_acquire(&lock->dep_map, 0, 0, 2, 2, NULL, (unsigned long)_RET_IP_);
+       return pthread_rwlock_rdlock(&lock->rwlock);
+
+}
+
+static inline int liblockdep_pthread_rwlock_unlock(liblockdep_pthread_rwlock_t *lock)
+{
+       lock_release(&lock->dep_map, 0, (unsigned long)_RET_IP_);
+       return pthread_rwlock_unlock(&lock->rwlock);
+}
+
+static inline int liblockdep_pthread_rwlock_wrlock(liblockdep_pthread_rwlock_t *lock)
+{
+       lock_acquire(&lock->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+       return pthread_rwlock_wrlock(&lock->rwlock);
+}
+
+static inline int liblockdep_pthread_rwlock_tryrdlock(liblockdep_pthread_rwlock_t *lock)
+{
+       lock_acquire(&lock->dep_map, 0, 1, 2, 2, NULL, (unsigned long)_RET_IP_);
+       return pthread_rwlock_tryrdlock(&lock->rwlock) == 0 ? 1 : 0;
+}
+
+static inline int liblockdep_pthread_rwlock_trywlock(liblockdep_pthread_rwlock_t *lock)
+{
+       lock_acquire(&lock->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+       return pthread_rwlock_trywlock(&lock->rwlock) == 0 ? 1 : 0;
+}
+
+static inline int liblockdep_rwlock_destroy(liblockdep_pthread_rwlock_t *lock)
+{
+       return pthread_rwlock_destroy(&lock->rwlock);
+}
+
+#ifdef __USE_LIBLOCKDEP
+
+#define pthread_rwlock_t               liblockdep_pthread_rwlock_t
+#define pthread_rwlock_init            liblockdep_pthread_rwlock_init
+#define pthread_rwlock_rdlock          liblockdep_pthread_rwlock_rdlock
+#define pthread_rwlock_unlock          liblockdep_pthread_rwlock_unlock
+#define pthread_rwlock_wrlock          liblockdep_pthread_rwlock_wrlock
+#define pthread_rwlock_tryrdlock       liblockdep_pthread_rwlock_tryrdlock
+#define pthread_rwlock_trywlock                liblockdep_pthread_rwlock_trywlock
+#define pthread_rwlock_destroy         liblockdep_rwlock_destroy
+
+#endif
+
+#endif
diff --git a/tools/lib/lockdep/lockdep b/tools/lib/lockdep/lockdep
new file mode 100755 (executable)
index 0000000..49af9fe
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+LD_PRELOAD="./liblockdep.so $LD_PRELOAD" "$@"
diff --git a/tools/lib/lockdep/lockdep.c b/tools/lib/lockdep/lockdep.c
new file mode 100644 (file)
index 0000000..f42b7e9
--- /dev/null
@@ -0,0 +1,2 @@
+#include <linux/lockdep.h>
+#include "../../../kernel/locking/lockdep.c"
diff --git a/tools/lib/lockdep/lockdep_internals.h b/tools/lib/lockdep/lockdep_internals.h
new file mode 100644 (file)
index 0000000..29d0c95
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../kernel/locking/lockdep_internals.h"
diff --git a/tools/lib/lockdep/lockdep_states.h b/tools/lib/lockdep/lockdep_states.h
new file mode 100644 (file)
index 0000000..248d235
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../kernel/locking/lockdep_states.h"
diff --git a/tools/lib/lockdep/preload.c b/tools/lib/lockdep/preload.c
new file mode 100644 (file)
index 0000000..f8465a8
--- /dev/null
@@ -0,0 +1,447 @@
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include "include/liblockdep/mutex.h"
+#include "../../../include/linux/rbtree.h"
+
+/**
+ * struct lock_lookup - liblockdep's view of a single unique lock
+ * @orig: pointer to the original pthread lock, used for lookups
+ * @dep_map: lockdep's dep_map structure
+ * @key: lockdep's key structure
+ * @node: rb-tree node used to store the lock in a global tree
+ * @name: a unique name for the lock
+ */
+struct lock_lookup {
+       void *orig; /* Original pthread lock, used for lookups */
+       struct lockdep_map dep_map; /* Since all locks are dynamic, we need
+                                    * a dep_map and a key for each lock */
+       /*
+        * Wait, there's no support for key classes? Yup :(
+        * Most big projects wrap the pthread api with their own calls to
+        * be compatible with different locking methods. This means that
+        * "classes" will be brokes since the function that creates all
+        * locks will point to a generic locking function instead of the
+        * actual code that wants to do the locking.
+        */
+       struct lock_class_key key;
+       struct rb_node node;
+#define LIBLOCKDEP_MAX_LOCK_NAME 22
+       char name[LIBLOCKDEP_MAX_LOCK_NAME];
+};
+
+/* This is where we store our locks */
+static struct rb_root locks = RB_ROOT;
+static pthread_rwlock_t locks_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* pthread mutex API */
+
+#ifdef __GLIBC__
+extern int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
+extern int __pthread_mutex_lock(pthread_mutex_t *mutex);
+extern int __pthread_mutex_trylock(pthread_mutex_t *mutex);
+extern int __pthread_mutex_unlock(pthread_mutex_t *mutex);
+extern int __pthread_mutex_destroy(pthread_mutex_t *mutex);
+#else
+#define __pthread_mutex_init   NULL
+#define __pthread_mutex_lock   NULL
+#define __pthread_mutex_trylock        NULL
+#define __pthread_mutex_unlock NULL
+#define __pthread_mutex_destroy        NULL
+#endif
+static int (*ll_pthread_mutex_init)(pthread_mutex_t *mutex,
+                       const pthread_mutexattr_t *attr)        = __pthread_mutex_init;
+static int (*ll_pthread_mutex_lock)(pthread_mutex_t *mutex)    = __pthread_mutex_lock;
+static int (*ll_pthread_mutex_trylock)(pthread_mutex_t *mutex) = __pthread_mutex_trylock;
+static int (*ll_pthread_mutex_unlock)(pthread_mutex_t *mutex)  = __pthread_mutex_unlock;
+static int (*ll_pthread_mutex_destroy)(pthread_mutex_t *mutex) = __pthread_mutex_destroy;
+
+/* pthread rwlock API */
+
+#ifdef __GLIBC__
+extern int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
+extern int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
+extern int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
+#else
+#define __pthread_rwlock_init          NULL
+#define __pthread_rwlock_destroy       NULL
+#define __pthread_rwlock_wrlock                NULL
+#define __pthread_rwlock_trywrlock     NULL
+#define __pthread_rwlock_rdlock                NULL
+#define __pthread_rwlock_tryrdlock     NULL
+#define __pthread_rwlock_unlock                NULL
+#endif
+
+static int (*ll_pthread_rwlock_init)(pthread_rwlock_t *rwlock,
+                       const pthread_rwlockattr_t *attr)               = __pthread_rwlock_init;
+static int (*ll_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock)      = __pthread_rwlock_destroy;
+static int (*ll_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock)       = __pthread_rwlock_rdlock;
+static int (*ll_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock)    = __pthread_rwlock_tryrdlock;
+static int (*ll_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock)    = __pthread_rwlock_trywrlock;
+static int (*ll_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock)       = __pthread_rwlock_wrlock;
+static int (*ll_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock)       = __pthread_rwlock_unlock;
+
+enum { none, prepare, done, } __init_state;
+static void init_preload(void);
+static void try_init_preload(void)
+{
+       if (!__init_state != done)
+               init_preload();
+}
+
+static struct rb_node **__get_lock_node(void *lock, struct rb_node **parent)
+{
+       struct rb_node **node = &locks.rb_node;
+       struct lock_lookup *l;
+
+       *parent = NULL;
+
+       while (*node) {
+               l = rb_entry(*node, struct lock_lookup, node);
+
+               *parent = *node;
+               if (lock < l->orig)
+                       node = &l->node.rb_left;
+               else if (lock > l->orig)
+                       node = &l->node.rb_right;
+               else
+                       return node;
+       }
+
+       return node;
+}
+
+#ifndef LIBLOCKDEP_STATIC_ENTRIES
+#define LIBLOCKDEP_STATIC_ENTRIES      1024
+#endif
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static struct lock_lookup __locks[LIBLOCKDEP_STATIC_ENTRIES];
+static int __locks_nr;
+
+static inline bool is_static_lock(struct lock_lookup *lock)
+{
+       return lock >= __locks && lock < __locks + ARRAY_SIZE(__locks);
+}
+
+static struct lock_lookup *alloc_lock(void)
+{
+       if (__init_state != done) {
+               /*
+                * Some programs attempt to initialize and use locks in their
+                * allocation path. This means that a call to malloc() would
+                * result in locks being initialized and locked.
+                *
+                * Why is it an issue for us? dlsym() below will try allocating
+                * to give us the original function. Since this allocation will
+                * result in a locking operations, we have to let pthread deal
+                * with it, but we can't! we don't have the pointer to the
+                * original API since we're inside dlsym() trying to get it
+                */
+
+               int idx = __locks_nr++;
+               if (idx >= ARRAY_SIZE(__locks)) {
+                       fprintf(stderr,
+               "LOCKDEP error: insufficient LIBLOCKDEP_STATIC_ENTRIES\n");
+                       exit(EX_UNAVAILABLE);
+               }
+               return __locks + idx;
+       }
+
+       return malloc(sizeof(struct lock_lookup));
+}
+
+static inline void free_lock(struct lock_lookup *lock)
+{
+       if (likely(!is_static_lock(lock)))
+               free(lock);
+}
+
+/**
+ * __get_lock - find or create a lock instance
+ * @lock: pointer to a pthread lock function
+ *
+ * Try to find an existing lock in the rbtree using the provided pointer. If
+ * one wasn't found - create it.
+ */
+static struct lock_lookup *__get_lock(void *lock)
+{
+       struct rb_node **node, *parent;
+       struct lock_lookup *l;
+
+       ll_pthread_rwlock_rdlock(&locks_rwlock);
+       node = __get_lock_node(lock, &parent);
+       ll_pthread_rwlock_unlock(&locks_rwlock);
+       if (*node) {
+               return rb_entry(*node, struct lock_lookup, node);
+       }
+
+       /* We didn't find the lock, let's create it */
+       l = alloc_lock();
+       if (l == NULL)
+               return NULL;
+
+       l->orig = lock;
+       /*
+        * Currently the name of the lock is the ptr value of the pthread lock,
+        * while not optimal, it makes debugging a bit easier.
+        *
+        * TODO: Get the real name of the lock using libdwarf
+        */
+       sprintf(l->name, "%p", lock);
+       lockdep_init_map(&l->dep_map, l->name, &l->key, 0);
+
+       ll_pthread_rwlock_wrlock(&locks_rwlock);
+       /* This might have changed since the last time we fetched it */
+       node = __get_lock_node(lock, &parent);
+       rb_link_node(&l->node, parent, node);
+       rb_insert_color(&l->node, &locks);
+       ll_pthread_rwlock_unlock(&locks_rwlock);
+
+       return l;
+}
+
+static void __del_lock(struct lock_lookup *lock)
+{
+       ll_pthread_rwlock_wrlock(&locks_rwlock);
+       rb_erase(&lock->node, &locks);
+       ll_pthread_rwlock_unlock(&locks_rwlock);
+       free_lock(lock);
+}
+
+int pthread_mutex_init(pthread_mutex_t *mutex,
+                       const pthread_mutexattr_t *attr)
+{
+       int r;
+
+       /*
+        * We keep trying to init our preload module because there might be
+        * code in init sections that tries to touch locks before we are
+        * initialized, in that case we'll need to manually call preload
+        * to get us going.
+        *
+        * Funny enough, kernel's lockdep had the same issue, and used
+        * (almost) the same solution. See look_up_lock_class() in
+        * kernel/locking/lockdep.c for details.
+        */
+       try_init_preload();
+
+       r = ll_pthread_mutex_init(mutex, attr);
+       if (r == 0)
+               /*
+                * We do a dummy initialization here so that lockdep could
+                * warn us if something fishy is going on - such as
+                * initializing a held lock.
+                */
+               __get_lock(mutex);
+
+       return r;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+       int r;
+
+       try_init_preload();
+
+       lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 2, NULL,
+                       (unsigned long)_RET_IP_);
+       /*
+        * Here's the thing with pthread mutexes: unlike the kernel variant,
+        * they can fail.
+        *
+        * This means that the behaviour here is a bit different from what's
+        * going on in the kernel: there we just tell lockdep that we took the
+        * lock before actually taking it, but here we must deal with the case
+        * that locking failed.
+        *
+        * To do that we'll "release" the lock if locking failed - this way
+        * we'll get lockdep doing the correct checks when we try to take
+        * the lock, and if that fails - we'll be back to the correct
+        * state by releasing it.
+        */
+       r = ll_pthread_mutex_lock(mutex);
+       if (r)
+               lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_mutex_trylock(pthread_mutex_t *mutex)
+{
+       int r;
+
+       try_init_preload();
+
+       lock_acquire(&__get_lock(mutex)->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+       r = ll_pthread_mutex_trylock(mutex);
+       if (r)
+               lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+       int r;
+
+       try_init_preload();
+
+       lock_release(&__get_lock(mutex)->dep_map, 0, (unsigned long)_RET_IP_);
+       /*
+        * Just like taking a lock, only in reverse!
+        *
+        * If we fail releasing the lock, tell lockdep we're holding it again.
+        */
+       r = ll_pthread_mutex_unlock(mutex);
+       if (r)
+               lock_acquire(&__get_lock(mutex)->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+       try_init_preload();
+
+       /*
+        * Let's see if we're releasing a lock that's held.
+        *
+        * TODO: Hook into free() and add that check there as well.
+        */
+       debug_check_no_locks_freed(mutex, mutex + sizeof(*mutex));
+       __del_lock(__get_lock(mutex));
+       return ll_pthread_mutex_destroy(mutex);
+}
+
+/* This is the rwlock part, very similar to what happened with mutex above */
+int pthread_rwlock_init(pthread_rwlock_t *rwlock,
+                       const pthread_rwlockattr_t *attr)
+{
+       int r;
+
+       try_init_preload();
+
+       r = ll_pthread_rwlock_init(rwlock, attr);
+       if (r == 0)
+               __get_lock(rwlock);
+
+       return r;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
+{
+       try_init_preload();
+
+       debug_check_no_locks_freed(rwlock, rwlock + sizeof(*rwlock));
+       __del_lock(__get_lock(rwlock));
+       return ll_pthread_rwlock_destroy(rwlock);
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
+{
+       int r;
+
+        init_preload();
+
+       lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 2, 2, NULL, (unsigned long)_RET_IP_);
+       r = ll_pthread_rwlock_rdlock(rwlock);
+       if (r)
+               lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
+{
+       int r;
+
+        init_preload();
+
+       lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 2, 2, NULL, (unsigned long)_RET_IP_);
+       r = ll_pthread_rwlock_tryrdlock(rwlock);
+       if (r)
+               lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
+{
+       int r;
+
+        init_preload();
+
+       lock_acquire(&__get_lock(rwlock)->dep_map, 0, 1, 0, 2, NULL, (unsigned long)_RET_IP_);
+       r = ll_pthread_rwlock_trywrlock(rwlock);
+       if (r)
+                lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
+{
+       int r;
+
+        init_preload();
+
+       lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+       r = ll_pthread_rwlock_wrlock(rwlock);
+       if (r)
+               lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
+{
+       int r;
+
+        init_preload();
+
+       lock_release(&__get_lock(rwlock)->dep_map, 0, (unsigned long)_RET_IP_);
+       r = ll_pthread_rwlock_unlock(rwlock);
+       if (r)
+               lock_acquire(&__get_lock(rwlock)->dep_map, 0, 0, 0, 2, NULL, (unsigned long)_RET_IP_);
+
+       return r;
+}
+
+__attribute__((constructor)) static void init_preload(void)
+{
+       if (__init_state != done)
+               return;
+
+#ifndef __GLIBC__
+       __init_state = prepare;
+
+       ll_pthread_mutex_init = dlsym(RTLD_NEXT, "pthread_mutex_init");
+       ll_pthread_mutex_lock = dlsym(RTLD_NEXT, "pthread_mutex_lock");
+       ll_pthread_mutex_trylock = dlsym(RTLD_NEXT, "pthread_mutex_trylock");
+       ll_pthread_mutex_unlock = dlsym(RTLD_NEXT, "pthread_mutex_unlock");
+       ll_pthread_mutex_destroy = dlsym(RTLD_NEXT, "pthread_mutex_destroy");
+
+       ll_pthread_rwlock_init = dlsym(RTLD_NEXT, "pthread_rwlock_init");
+       ll_pthread_rwlock_destroy = dlsym(RTLD_NEXT, "pthread_rwlock_destroy");
+       ll_pthread_rwlock_rdlock = dlsym(RTLD_NEXT, "pthread_rwlock_rdlock");
+       ll_pthread_rwlock_tryrdlock = dlsym(RTLD_NEXT, "pthread_rwlock_tryrdlock");
+       ll_pthread_rwlock_wrlock = dlsym(RTLD_NEXT, "pthread_rwlock_wrlock");
+       ll_pthread_rwlock_trywrlock = dlsym(RTLD_NEXT, "pthread_rwlock_trywrlock");
+       ll_pthread_rwlock_unlock = dlsym(RTLD_NEXT, "pthread_rwlock_unlock");
+#endif
+
+       printf("%p\n", ll_pthread_mutex_trylock);fflush(stdout);
+
+       lockdep_init();
+
+       __init_state = done;
+}
diff --git a/tools/lib/lockdep/rbtree.c b/tools/lib/lockdep/rbtree.c
new file mode 100644 (file)
index 0000000..f7f4303
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../lib/rbtree.c"
diff --git a/tools/lib/lockdep/run_tests.sh b/tools/lib/lockdep/run_tests.sh
new file mode 100644 (file)
index 0000000..5334ad9
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/bash
+
+make &> /dev/null
+
+for i in `ls tests/*.c`; do
+       testname=$(basename -s .c "$i")
+       gcc -o tests/$testname -pthread -lpthread $i liblockdep.a -Iinclude -D__USE_LIBLOCKDEP &> /dev/null
+       echo -ne "$testname... "
+       if [ $(timeout 1 ./tests/$testname | wc -l) -gt 0 ]; then
+               echo "PASSED!"
+       else
+               echo "FAILED!"
+       fi
+       rm tests/$testname
+done
+
+for i in `ls tests/*.c`; do
+       testname=$(basename -s .c "$i")
+       gcc -o tests/$testname -pthread -lpthread -Iinclude $i &> /dev/null
+       echo -ne "(PRELOAD) $testname... "
+       if [ $(timeout 1 ./lockdep ./tests/$testname | wc -l) -gt 0 ]; then
+               echo "PASSED!"
+       else
+               echo "FAILED!"
+       fi
+       rm tests/$testname
+done
diff --git a/tools/lib/lockdep/tests/AA.c b/tools/lib/lockdep/tests/AA.c
new file mode 100644 (file)
index 0000000..0f782ff
--- /dev/null
@@ -0,0 +1,13 @@
+#include <liblockdep/mutex.h>
+
+void main(void)
+{
+       pthread_mutex_t a, b;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+
+       pthread_mutex_lock(&a);
+       pthread_mutex_lock(&b);
+       pthread_mutex_lock(&a);
+}
diff --git a/tools/lib/lockdep/tests/ABBA.c b/tools/lib/lockdep/tests/ABBA.c
new file mode 100644 (file)
index 0000000..07f0e29
--- /dev/null
@@ -0,0 +1,13 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+       pthread_mutex_t a, b;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+
+       LOCK_UNLOCK_2(a, b);
+       LOCK_UNLOCK_2(b, a);
+}
diff --git a/tools/lib/lockdep/tests/ABBCCA.c b/tools/lib/lockdep/tests/ABBCCA.c
new file mode 100644 (file)
index 0000000..843db09
--- /dev/null
@@ -0,0 +1,15 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+       pthread_mutex_t a, b, c;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+       pthread_mutex_init(&c, NULL);
+
+       LOCK_UNLOCK_2(a, b);
+       LOCK_UNLOCK_2(b, c);
+       LOCK_UNLOCK_2(c, a);
+}
diff --git a/tools/lib/lockdep/tests/ABBCCDDA.c b/tools/lib/lockdep/tests/ABBCCDDA.c
new file mode 100644 (file)
index 0000000..33620e2
--- /dev/null
@@ -0,0 +1,17 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+       pthread_mutex_t a, b, c, d;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+       pthread_mutex_init(&c, NULL);
+       pthread_mutex_init(&d, NULL);
+
+       LOCK_UNLOCK_2(a, b);
+       LOCK_UNLOCK_2(b, c);
+       LOCK_UNLOCK_2(c, d);
+       LOCK_UNLOCK_2(d, a);
+}
diff --git a/tools/lib/lockdep/tests/ABCABC.c b/tools/lib/lockdep/tests/ABCABC.c
new file mode 100644 (file)
index 0000000..3fee51e
--- /dev/null
@@ -0,0 +1,15 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+       pthread_mutex_t a, b, c;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+       pthread_mutex_init(&c, NULL);
+
+       LOCK_UNLOCK_2(a, b);
+       LOCK_UNLOCK_2(c, a);
+       LOCK_UNLOCK_2(b, c);
+}
diff --git a/tools/lib/lockdep/tests/ABCDBCDA.c b/tools/lib/lockdep/tests/ABCDBCDA.c
new file mode 100644 (file)
index 0000000..427ba56
--- /dev/null
@@ -0,0 +1,17 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+       pthread_mutex_t a, b, c, d;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+       pthread_mutex_init(&c, NULL);
+       pthread_mutex_init(&d, NULL);
+
+       LOCK_UNLOCK_2(a, b);
+       LOCK_UNLOCK_2(c, d);
+       LOCK_UNLOCK_2(b, c);
+       LOCK_UNLOCK_2(d, a);
+}
diff --git a/tools/lib/lockdep/tests/ABCDBDDA.c b/tools/lib/lockdep/tests/ABCDBDDA.c
new file mode 100644 (file)
index 0000000..680c6cf
--- /dev/null
@@ -0,0 +1,17 @@
+#include <liblockdep/mutex.h>
+#include "common.h"
+
+void main(void)
+{
+       pthread_mutex_t a, b, c, d;
+
+       pthread_mutex_init(&a, NULL);
+       pthread_mutex_init(&b, NULL);
+       pthread_mutex_init(&c, NULL);
+       pthread_mutex_init(&d, NULL);
+
+       LOCK_UNLOCK_2(a, b);
+       LOCK_UNLOCK_2(c, d);
+       LOCK_UNLOCK_2(b, d);
+       LOCK_UNLOCK_2(d, a);
+}
diff --git a/tools/lib/lockdep/tests/WW.c b/tools/lib/lockdep/tests/WW.c
new file mode 100644 (file)
index 0000000..d44f77d
--- /dev/null
@@ -0,0 +1,13 @@
+#include <liblockdep/rwlock.h>
+
+void main(void)
+{
+       pthread_rwlock_t a, b;
+
+       pthread_rwlock_init(&a, NULL);
+       pthread_rwlock_init(&b, NULL);
+
+       pthread_rwlock_wrlock(&a);
+       pthread_rwlock_rdlock(&b);
+       pthread_rwlock_wrlock(&a);
+}
diff --git a/tools/lib/lockdep/tests/common.h b/tools/lib/lockdep/tests/common.h
new file mode 100644 (file)
index 0000000..d89e94d
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _LIBLOCKDEP_TEST_COMMON_H
+#define _LIBLOCKDEP_TEST_COMMON_H
+
+#define LOCK_UNLOCK_2(a, b)                    \
+       do {                                    \
+               pthread_mutex_lock(&(a));       \
+               pthread_mutex_lock(&(b));       \
+               pthread_mutex_unlock(&(b));     \
+               pthread_mutex_unlock(&(a));     \
+       } while(0)
+
+#endif
diff --git a/tools/lib/lockdep/tests/unlock_balance.c b/tools/lib/lockdep/tests/unlock_balance.c
new file mode 100644 (file)
index 0000000..0bc62de
--- /dev/null
@@ -0,0 +1,12 @@
+#include <liblockdep/mutex.h>
+
+void main(void)
+{
+       pthread_mutex_t a;
+
+       pthread_mutex_init(&a, NULL);
+
+       pthread_mutex_lock(&a);
+       pthread_mutex_unlock(&a);
+       pthread_mutex_unlock(&a);
+}
diff --git a/tools/lib/lockdep/uinclude/asm/hweight.h b/tools/lib/lockdep/uinclude/asm/hweight.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/asm/sections.h b/tools/lib/lockdep/uinclude/asm/sections.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/bitops.h b/tools/lib/lockdep/uinclude/linux/bitops.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/compiler.h b/tools/lib/lockdep/uinclude/linux/compiler.h
new file mode 100644 (file)
index 0000000..7ac838a
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _LIBLOCKDEP_LINUX_COMPILER_H_
+#define _LIBLOCKDEP_LINUX_COMPILER_H_
+
+#define __used         __attribute__((__unused__))
+#define unlikely
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/debug_locks.h b/tools/lib/lockdep/uinclude/linux/debug_locks.h
new file mode 100644 (file)
index 0000000..f38eb64
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _LIBLOCKDEP_DEBUG_LOCKS_H_
+#define _LIBLOCKDEP_DEBUG_LOCKS_H_
+
+#include <stddef.h>
+#include <linux/compiler.h>
+
+#define DEBUG_LOCKS_WARN_ON(x) (x)
+
+extern bool debug_locks;
+extern bool debug_locks_silent;
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/delay.h b/tools/lib/lockdep/uinclude/linux/delay.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/export.h b/tools/lib/lockdep/uinclude/linux/export.h
new file mode 100644 (file)
index 0000000..6bdf349
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _LIBLOCKDEP_LINUX_EXPORT_H_
+#define _LIBLOCKDEP_LINUX_EXPORT_H_
+
+#define EXPORT_SYMBOL(sym)
+#define EXPORT_SYMBOL_GPL(sym)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/ftrace.h b/tools/lib/lockdep/uinclude/linux/ftrace.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/gfp.h b/tools/lib/lockdep/uinclude/linux/gfp.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/hardirq.h b/tools/lib/lockdep/uinclude/linux/hardirq.h
new file mode 100644 (file)
index 0000000..c8f3f8f
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _LIBLOCKDEP_LINUX_HARDIRQ_H_
+#define _LIBLOCKDEP_LINUX_HARDIRQ_H_
+
+#define SOFTIRQ_BITS   0UL
+#define HARDIRQ_BITS   0UL
+#define SOFTIRQ_SHIFT  0UL
+#define HARDIRQ_SHIFT  0UL
+#define hardirq_count()        0UL
+#define softirq_count()        0UL
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/hash.h b/tools/lib/lockdep/uinclude/linux/hash.h
new file mode 100644 (file)
index 0000000..0f84798
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../include/linux/hash.h"
diff --git a/tools/lib/lockdep/uinclude/linux/interrupt.h b/tools/lib/lockdep/uinclude/linux/interrupt.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/irqflags.h b/tools/lib/lockdep/uinclude/linux/irqflags.h
new file mode 100644 (file)
index 0000000..6cc296f
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_
+#define _LIBLOCKDEP_LINUX_TRACE_IRQFLAGS_H_
+
+# define trace_hardirq_context(p)      0
+# define trace_softirq_context(p)      0
+# define trace_hardirqs_enabled(p)     0
+# define trace_softirqs_enabled(p)     0
+# define trace_hardirq_enter()         do { } while (0)
+# define trace_hardirq_exit()          do { } while (0)
+# define lockdep_softirq_enter()       do { } while (0)
+# define lockdep_softirq_exit()                do { } while (0)
+# define INIT_TRACE_IRQFLAGS
+
+# define stop_critical_timings() do { } while (0)
+# define start_critical_timings() do { } while (0)
+
+#define raw_local_irq_disable() do { } while (0)
+#define raw_local_irq_enable() do { } while (0)
+#define raw_local_irq_save(flags) ((flags) = 0)
+#define raw_local_irq_restore(flags) do { } while (0)
+#define raw_local_save_flags(flags) ((flags) = 0)
+#define raw_irqs_disabled_flags(flags) do { } while (0)
+#define raw_irqs_disabled() 0
+#define raw_safe_halt()
+
+#define local_irq_enable() do { } while (0)
+#define local_irq_disable() do { } while (0)
+#define local_irq_save(flags) ((flags) = 0)
+#define local_irq_restore(flags) do { } while (0)
+#define local_save_flags(flags)        ((flags) = 0)
+#define irqs_disabled() (1)
+#define irqs_disabled_flags(flags) (0)
+#define safe_halt() do { } while (0)
+
+#define trace_lock_release(x, y)
+#define trace_lock_acquire(a, b, c, d, e, f, g)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kallsyms.h b/tools/lib/lockdep/uinclude/linux/kallsyms.h
new file mode 100644 (file)
index 0000000..b0f2dbd
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _LIBLOCKDEP_LINUX_KALLSYMS_H_
+#define _LIBLOCKDEP_LINUX_KALLSYMS_H_
+
+#include <linux/kernel.h>
+#include <stdio.h>
+
+#define KSYM_NAME_LEN 128
+
+struct module;
+
+static inline const char *kallsyms_lookup(unsigned long addr,
+                                         unsigned long *symbolsize,
+                                         unsigned long *offset,
+                                         char **modname, char *namebuf)
+{
+       return NULL;
+}
+
+#include <execinfo.h>
+#include <stdlib.h>
+static inline void print_ip_sym(unsigned long ip)
+{
+       char **name;
+
+       name = backtrace_symbols((void **)&ip, 1);
+
+       printf("%s\n", *name);
+
+       free(name);
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kern_levels.h b/tools/lib/lockdep/uinclude/linux/kern_levels.h
new file mode 100644 (file)
index 0000000..3b9bade
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __KERN_LEVELS_H__
+#define __KERN_LEVELS_H__
+
+#define KERN_SOH       ""              /* ASCII Start Of Header */
+#define KERN_SOH_ASCII ''
+
+#define KERN_EMERG     KERN_SOH ""     /* system is unusable */
+#define KERN_ALERT     KERN_SOH ""     /* action must be taken immediately */
+#define KERN_CRIT      KERN_SOH ""     /* critical conditions */
+#define KERN_ERR       KERN_SOH ""     /* error conditions */
+#define KERN_WARNING   KERN_SOH ""     /* warning conditions */
+#define KERN_NOTICE    KERN_SOH ""     /* normal but significant condition */
+#define KERN_INFO      KERN_SOH ""     /* informational */
+#define KERN_DEBUG     KERN_SOH ""     /* debug-level messages */
+
+#define KERN_DEFAULT   KERN_SOH ""     /* the default kernel loglevel */
+
+/*
+ * Annotation for a "continued" line of log printout (only done after a
+ * line that had no enclosing \n). Only to be used by core/arch code
+ * during early bootup (a continued line is not SMP-safe otherwise).
+ */
+#define KERN_CONT      ""
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kernel.h b/tools/lib/lockdep/uinclude/linux/kernel.h
new file mode 100644 (file)
index 0000000..a11e3c3
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef _LIBLOCKDEP_LINUX_KERNEL_H_
+#define _LIBLOCKDEP_LINUX_KERNEL_H_
+
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/rcu.h>
+#include <linux/hardirq.h>
+#include <linux/kern_levels.h>
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                     \
+       const typeof(((type *)0)->member) * __mptr = (ptr);     \
+       (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#define max(x, y) ({                           \
+       typeof(x) _max1 = (x);                  \
+       typeof(y) _max2 = (y);                  \
+       (void) (&_max1 == &_max2);              \
+       _max1 > _max2 ? _max1 : _max2; })
+
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+#define WARN_ON(x) (x)
+#define WARN_ON_ONCE(x) (x)
+#define likely(x) (x)
+#define WARN(x, y, z) (x)
+#define uninitialized_var(x) x
+#define __init
+#define noinline
+#define list_add_tail_rcu list_add_tail
+
+#ifndef CALLER_ADDR0
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+#endif
+
+#ifndef _RET_IP_
+#define _RET_IP_ CALLER_ADDR0
+#endif
+
+#ifndef _THIS_IP_
+#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })
+#endif
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/kmemcheck.h b/tools/lib/lockdep/uinclude/linux/kmemcheck.h
new file mode 100644 (file)
index 0000000..94d598b
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _LIBLOCKDEP_LINUX_KMEMCHECK_H_
+#define _LIBLOCKDEP_LINUX_KMEMCHECK_H_
+
+static inline void kmemcheck_mark_initialized(void *address, unsigned int n)
+{
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/linkage.h b/tools/lib/lockdep/uinclude/linux/linkage.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/list.h b/tools/lib/lockdep/uinclude/linux/list.h
new file mode 100644 (file)
index 0000000..6e9ef31
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../include/linux/list.h"
diff --git a/tools/lib/lockdep/uinclude/linux/lockdep.h b/tools/lib/lockdep/uinclude/linux/lockdep.h
new file mode 100644 (file)
index 0000000..d0f5d6e
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _LIBLOCKDEP_LOCKDEP_H_
+#define _LIBLOCKDEP_LOCKDEP_H_
+
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <string.h>
+#include <limits.h>
+#include <linux/utsname.h>
+
+
+#define MAX_LOCK_DEPTH 2000UL
+
+#include "../../../include/linux/lockdep.h"
+
+struct task_struct {
+       u64 curr_chain_key;
+       int lockdep_depth;
+       unsigned int lockdep_recursion;
+       struct held_lock held_locks[MAX_LOCK_DEPTH];
+       gfp_t lockdep_reclaim_gfp;
+       int pid;
+       char comm[17];
+};
+
+extern struct task_struct *__curr(void);
+
+#define current (__curr())
+
+#define debug_locks_off() 1
+#define task_pid_nr(tsk) ((tsk)->pid)
+
+#define KSYM_NAME_LEN 128
+#define printk printf
+
+#define list_del_rcu list_del
+
+#define atomic_t unsigned long
+#define atomic_inc(x) ((*(x))++)
+
+static struct new_utsname *init_utsname(void)
+{
+       static struct new_utsname n = (struct new_utsname) {
+               .release = "liblockdep",
+               .version = LIBLOCKDEP_VERSION,
+       };
+
+       return &n;
+}
+
+#define print_tainted() ""
+#define static_obj(x) 1
+
+#define debug_show_all_locks()
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/module.h b/tools/lib/lockdep/uinclude/linux/module.h
new file mode 100644 (file)
index 0000000..09c7a7b
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _LIBLOCKDEP_LINUX_MODULE_H_
+#define _LIBLOCKDEP_LINUX_MODULE_H_
+
+#define module_param(name, type, perm)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/mutex.h b/tools/lib/lockdep/uinclude/linux/mutex.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/poison.h b/tools/lib/lockdep/uinclude/linux/poison.h
new file mode 100644 (file)
index 0000000..0c27bdf
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../include/linux/poison.h"
diff --git a/tools/lib/lockdep/uinclude/linux/prefetch.h b/tools/lib/lockdep/uinclude/linux/prefetch.h
new file mode 100644 (file)
index 0000000..d73fe6f
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _LIBLOCKDEP_LINUX_PREFETCH_H_
+#define _LIBLOCKDEP_LINUX_PREFETCH_H
+
+static inline void prefetch(void *a __attribute__((unused))) { }
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/proc_fs.h b/tools/lib/lockdep/uinclude/linux/proc_fs.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/rbtree.h b/tools/lib/lockdep/uinclude/linux/rbtree.h
new file mode 100644 (file)
index 0000000..965901d
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../include/linux/rbtree.h"
diff --git a/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h b/tools/lib/lockdep/uinclude/linux/rbtree_augmented.h
new file mode 100644 (file)
index 0000000..c375947
--- /dev/null
@@ -0,0 +1,2 @@
+#define __always_inline
+#include "../../../include/linux/rbtree_augmented.h"
diff --git a/tools/lib/lockdep/uinclude/linux/rcu.h b/tools/lib/lockdep/uinclude/linux/rcu.h
new file mode 100644 (file)
index 0000000..4c99fcb
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _LIBLOCKDEP_RCU_H_
+#define _LIBLOCKDEP_RCU_H_
+
+int rcu_scheduler_active;
+
+static inline int rcu_lockdep_current_cpu_online(void)
+{
+       return 1;
+}
+
+static inline int rcu_is_cpu_idle(void)
+{
+       return 1;
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/seq_file.h b/tools/lib/lockdep/uinclude/linux/seq_file.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
diff --git a/tools/lib/lockdep/uinclude/linux/spinlock.h b/tools/lib/lockdep/uinclude/linux/spinlock.h
new file mode 100644 (file)
index 0000000..68c1aa2
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _LIBLOCKDEP_SPINLOCK_H_
+#define _LIBLOCKDEP_SPINLOCK_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#define arch_spinlock_t pthread_mutex_t
+#define __ARCH_SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER
+
+static inline void arch_spin_lock(arch_spinlock_t *mutex)
+{
+       pthread_mutex_lock(mutex);
+}
+
+static inline void arch_spin_unlock(arch_spinlock_t *mutex)
+{
+       pthread_mutex_unlock(mutex);
+}
+
+static inline bool arch_spin_is_locked(arch_spinlock_t *mutex)
+{
+       return true;
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/stacktrace.h b/tools/lib/lockdep/uinclude/linux/stacktrace.h
new file mode 100644 (file)
index 0000000..39aecc6
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _LIBLOCKDEP_LINUX_STACKTRACE_H_
+#define _LIBLOCKDEP_LINUX_STACKTRACE_H_
+
+#include <execinfo.h>
+
+struct stack_trace {
+       unsigned int nr_entries, max_entries;
+       unsigned long *entries;
+       int skip;
+};
+
+static inline void print_stack_trace(struct stack_trace *trace, int spaces)
+{
+       backtrace_symbols_fd((void **)trace->entries, trace->nr_entries, 1);
+}
+
+#define save_stack_trace(trace)        \
+       ((trace)->nr_entries =  \
+               backtrace((void **)(trace)->entries, (trace)->max_entries))
+
+static inline int dump_stack(void)
+{
+       void *array[64];
+       size_t size;
+
+       size = backtrace(array, 64);
+       backtrace_symbols_fd(array, size, 1);
+
+       return 0;
+}
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/stringify.h b/tools/lib/lockdep/uinclude/linux/stringify.h
new file mode 100644 (file)
index 0000000..05dfcd1
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _LIBLOCKDEP_LINUX_STRINGIFY_H_
+#define _LIBLOCKDEP_LINUX_STRINGIFY_H_
+
+#define __stringify_1(x...)    #x
+#define __stringify(x...)      __stringify_1(x)
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/linux/types.h b/tools/lib/lockdep/uinclude/linux/types.h
new file mode 100644 (file)
index 0000000..929938f
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef _LIBLOCKDEP_LINUX_TYPES_H_
+#define _LIBLOCKDEP_LINUX_TYPES_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#define __SANE_USERSPACE_TYPES__       /* For PPC64, to get LL64 types */
+#include <asm/types.h>
+
+struct page;
+struct kmem_cache;
+
+typedef unsigned gfp_t;
+
+typedef __u64 u64;
+typedef __s64 s64;
+
+typedef __u32 u32;
+typedef __s32 s32;
+
+typedef __u16 u16;
+typedef __s16 s16;
+
+typedef __u8  u8;
+typedef __s8  s8;
+
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#endif
diff --git a/tools/lib/lockdep/uinclude/trace/events/lock.h b/tools/lib/lockdep/uinclude/trace/events/lock.h
new file mode 100644 (file)
index 0000000..fab00ff
--- /dev/null
@@ -0,0 +1,3 @@
+
+/* empty file */
+
index 0362d575de7d185752b2a23a53845236c71510f6..900fca01bdd3fcbc8617929a822ba5d90ba47ae0 100644 (file)
@@ -1606,6 +1606,24 @@ process_arg(struct event_format *event, struct print_arg *arg, char **tok)
 static enum event_type
 process_op(struct event_format *event, struct print_arg *arg, char **tok);
 
+/*
+ * For __print_symbolic() and __print_flags, we need to completely
+ * evaluate the first argument, which defines what to print next.
+ */
+static enum event_type
+process_field_arg(struct event_format *event, struct print_arg *arg, char **tok)
+{
+       enum event_type type;
+
+       type = process_arg(event, arg, tok);
+
+       while (type == EVENT_OP) {
+               type = process_op(event, arg, tok);
+       }
+
+       return type;
+}
+
 static enum event_type
 process_cond(struct event_format *event, struct print_arg *top, char **tok)
 {
@@ -2371,7 +2389,7 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok)
                goto out_free;
        }
 
-       type = process_arg(event, field, &token);
+       type = process_field_arg(event, field, &token);
 
        /* Handle operations in the first argument */
        while (type == EVENT_OP)
@@ -2424,7 +2442,8 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
                goto out_free;
        }
 
-       type = process_arg(event, field, &token);
+       type = process_field_arg(event, field, &token);
+
        if (test_type_token(type, token, EVENT_DELIM, ","))
                goto out_free_field;
 
@@ -3446,7 +3465,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
                 * is in the bottom half of the 32 bit field.
                 */
                offset &= 0xffff;
-               val = (unsigned long long)(data + offset);
+               val = (unsigned long long)((unsigned long)data + offset);
                break;
        default: /* not sure what to do there */
                return 0;
@@ -4080,6 +4099,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
        unsigned long long val;
        struct func_map *func;
        const char *saveptr;
+       struct trace_seq p;
        char *bprint_fmt = NULL;
        char format[32];
        int show_func;
@@ -4287,8 +4307,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
                                format[len] = 0;
                                if (!len_as_arg)
                                        len_arg = -1;
-                               print_str_arg(s, data, size, event,
+                               /* Use helper trace_seq */
+                               trace_seq_init(&p);
+                               print_str_arg(&p, data, size, event,
                                              format, len_arg, arg);
+                               trace_seq_terminate(&p);
+                               trace_seq_puts(s, p.buffer);
                                arg = arg->next;
                                break;
                        default:
index 052f7c4dc00c25a47dbe6355f0f762744ad5a79a..c407897f04359c9e253fbcb27368926419aced51 100644 (file)
@@ -57,6 +57,8 @@ OPTIONS
 -t::
 --tid=::
         Record events on existing thread ID (comma separated list).
+        This option also disables inheritance by default.  Enable it by adding
+        --inherit.
 
 -u::
 --uid=::
@@ -201,6 +203,12 @@ abort events and some memory events in precise mode on modern Intel CPUs.
 --transaction::
 Record transaction flags for transaction related events.
 
+--per-thread::
+Use per-thread mmaps.  By default per-cpu mmaps are created.  This option
+overrides that and uses per-thread mmaps.  A side-effect of that is that
+inheritance is automatically disabled.  --per-thread is ignored with a warning
+if combined with -a or -C options.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1]
index e9cbfcddfa3f608ee19b2366802f7f405f78f535..cfdbb1e045b53514f919e6c96acb48d0b6df4be3 100644 (file)
@@ -203,6 +203,12 @@ OPTIONS
 --show-kernel-path::
        Try to resolve the path of [kernel.kallsyms]
 
+--show-task-events
+       Display task related events (e.g. FORK, COMM, EXIT).
+
+--show-mmap-events
+       Display mmap related events (e.g. MMAP, MMAP2).
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
index 3ff8bd4f0b4d94163d77231881b462e50bbd4e17..271dd4ed5b05bc8abf19699f1eef64f26b4f7d3b 100644 (file)
@@ -8,8 +8,7 @@ perf-timechart - Tool to visualize total system behavior during a workload
 SYNOPSIS
 --------
 [verse]
-'perf timechart' record <command>
-'perf timechart' [<options>]
+'perf timechart' [<timechart options>] {record} [<record options>]
 
 DESCRIPTION
 -----------
@@ -21,8 +20,8 @@ There are two variants of perf timechart:
   'perf timechart' to turn a trace into a Scalable Vector Graphics file,
   that can be viewed with popular SVG viewers such as 'Inkscape'.
 
-OPTIONS
--------
+TIMECHART OPTIONS
+-----------------
 -o::
 --output=::
         Select the output file (default: output.svg)
@@ -35,6 +34,9 @@ OPTIONS
 -P::
 --power-only::
         Only output the CPU power section of the diagram
+-T::
+--tasks-only::
+        Don't output processor state transitions
 -p::
 --process::
         Select the processes to display, by name or PID
@@ -54,6 +56,22 @@ $ perf timechart
 
   Written 10.2 seconds of trace to output.svg.
 
+-n::
+--proc-num::
+        Print task info for at least given number of tasks.
+
+RECORD OPTIONS
+--------------
+-P::
+--power-only::
+        Record only power-related events
+-T::
+--tasks-only::
+        Record only tasks-related events
+-g::
+--callchain::
+        Do call-graph (stack chain/backtrace) recording
+
 SEE ALSO
 --------
 linkperf:perf-record[1]
index 7de01dd7968898c18e34f40914017ae549a3f618..cdd8d4946dba8f85b6ef6f56062067dd1b2dd622 100644 (file)
@@ -50,7 +50,6 @@ Default is to monitor all CPUS.
 --count-filter=<count>::
        Only display functions with more events than this.
 
--g::
 --group::
         Put the counters into a counter group.
 
@@ -143,12 +142,12 @@ Default is to monitor all CPUS.
 --asm-raw::
        Show raw instruction encoding of assembly instructions.
 
--G::
+-g::
        Enables call-graph (stack chain/backtrace) recording.
 
 --call-graph::
        Setup and enable call-graph (stack chain/backtrace) recording,
-       implies -G.
+       implies -g.
 
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
index 4835618a5608892054de87d497e45e644fdeb0da..eefb9fb0c02f991ba00007b8885685ff248a85dc 100644 (file)
@@ -60,8 +60,11 @@ endef
 
 #
 # Needed if no target specified:
+# (Except for tags and TAGS targets. The reason is that the
+# Makefile does not treat tags/TAGS as targets but as files
+# and thus won't rebuilt them once they are in place.)
 #
-all:
+all tags TAGS:
        $(print_msg)
        $(make)
 
@@ -77,3 +80,5 @@ clean:
 %:
        $(print_msg)
        $(make)
+
+.PHONY: tags TAGS
index 7fc8f179cae74d08e2cc507031c773009723bb00..e416ccc7d83180d203e7dd0cea0f64cc49819fec 100644 (file)
@@ -840,9 +840,9 @@ ifndef NO_LIBPYTHON
                $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'; \
                $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
 endif
-       $(call QUIET_INSTALL, bash_completion-script) \
+       $(call QUIET_INSTALL, perf_completion-script) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \
-               $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
+               $(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
        $(call QUIET_INSTALL, tests) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
                $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
index 89acc17cf2a02e4c7d8760a7b89e1300007cc5a3..6ea9e85bdc00ca02a50735abf0ac304107dd1727 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 4d644fe2d5b78315f6d00f0d66560ae5836e7b02..65615a8bc25e7ff02a1a96f78114decd54df6212 100644 (file)
@@ -800,6 +800,7 @@ static struct perf_record record = {
                .freq                = 4000,
                .target              = {
                        .uses_mmap   = true,
+                       .default_per_cpu = true,
                },
        },
 };
@@ -842,8 +843,9 @@ const struct option record_options[] = {
        OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
        OPT_STRING('o', "output", &record.file.path, "file",
                    "output file name"),
-       OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit,
-                   "child tasks do not inherit counters"),
+       OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit,
+                       &record.opts.no_inherit_set,
+                       "child tasks do not inherit counters"),
        OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
        OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages",
                     "number of mmap data pages",
@@ -888,6 +890,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, "per-thread", &record.opts.target.per_thread,
+                   "use per-thread mmaps"),
        OPT_END()
 };
 
@@ -936,6 +940,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
                goto out_symbol_exit;
        }
 
+       if (rec->opts.target.tid && !rec->opts.no_inherit_set)
+               rec->opts.no_inherit = true;
+
        err = target__validate(&rec->opts.target);
        if (err) {
                target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
index baf17989a216137064e089d4e2f65fef07a4357f..952dce979252f5e9d71f8093d0b71f4367c0a1f5 100644 (file)
@@ -280,6 +280,30 @@ static int perf_session__check_output_opt(struct perf_session *session)
                set_print_ip_opts(&evsel->attr);
        }
 
+       /*
+        * set default for tracepoints to print symbols only
+        * if callchains are present
+        */
+       if (symbol_conf.use_callchain &&
+           !output[PERF_TYPE_TRACEPOINT].user_set) {
+               struct perf_event_attr *attr;
+
+               j = PERF_TYPE_TRACEPOINT;
+               evsel = perf_session__find_first_evtype(session, j);
+               if (evsel == NULL)
+                       goto out;
+
+               attr = &evsel->attr;
+
+               if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
+                       output[j].fields |= PERF_OUTPUT_IP;
+                       output[j].fields |= PERF_OUTPUT_SYM;
+                       output[j].fields |= PERF_OUTPUT_DSO;
+                       set_print_ip_opts(attr);
+               }
+       }
+
+out:
        return 0;
 }
 
@@ -288,7 +312,6 @@ static void print_sample_start(struct perf_sample *sample,
                               struct perf_evsel *evsel)
 {
        struct perf_event_attr *attr = &evsel->attr;
-       const char *evname = NULL;
        unsigned long secs;
        unsigned long usecs;
        unsigned long long nsecs;
@@ -323,11 +346,6 @@ static void print_sample_start(struct perf_sample *sample,
                usecs = nsecs / NSECS_PER_USEC;
                printf("%5lu.%06lu: ", secs, usecs);
        }
-
-       if (PRINT_FIELD(EVNAME)) {
-               evname = perf_evsel__name(evsel);
-               printf("%s: ", evname ? evname : "[unknown]");
-       }
 }
 
 static bool is_bts_event(struct perf_event_attr *attr)
@@ -434,6 +452,11 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
 
        print_sample_start(sample, thread, evsel);
 
+       if (PRINT_FIELD(EVNAME)) {
+               const char *evname = perf_evsel__name(evsel);
+               printf("%s: ", evname ? evname : "[unknown]");
+       }
+
        if (is_bts_event(attr)) {
                print_sample_bts(event, sample, evsel, machine, thread);
                return;
@@ -549,6 +572,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
 struct perf_script {
        struct perf_tool        tool;
        struct perf_session     *session;
+       bool                    show_task_events;
+       bool                    show_mmap_events;
 };
 
 static int process_attr(struct perf_tool *tool, union perf_event *event,
@@ -579,6 +604,163 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
        return perf_evsel__check_attr(evsel, scr->session);
 }
 
+static int process_comm_event(struct perf_tool *tool,
+                             union perf_event *event,
+                             struct perf_sample *sample,
+                             struct machine *machine)
+{
+       struct thread *thread;
+       struct perf_script *script = container_of(tool, struct perf_script, tool);
+       struct perf_session *session = script->session;
+       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+       int ret = -1;
+
+       thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid);
+       if (thread == NULL) {
+               pr_debug("problem processing COMM event, skipping it.\n");
+               return -1;
+       }
+
+       if (perf_event__process_comm(tool, event, sample, machine) < 0)
+               goto out;
+
+       if (!evsel->attr.sample_id_all) {
+               sample->cpu = 0;
+               sample->time = 0;
+               sample->tid = event->comm.tid;
+               sample->pid = event->comm.pid;
+       }
+       print_sample_start(sample, thread, evsel);
+       perf_event__fprintf(event, stdout);
+       ret = 0;
+
+out:
+       return ret;
+}
+
+static int process_fork_event(struct perf_tool *tool,
+                             union perf_event *event,
+                             struct perf_sample *sample,
+                             struct machine *machine)
+{
+       struct thread *thread;
+       struct perf_script *script = container_of(tool, struct perf_script, tool);
+       struct perf_session *session = script->session;
+       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+
+       if (perf_event__process_fork(tool, event, sample, machine) < 0)
+               return -1;
+
+       thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
+       if (thread == NULL) {
+               pr_debug("problem processing FORK event, skipping it.\n");
+               return -1;
+       }
+
+       if (!evsel->attr.sample_id_all) {
+               sample->cpu = 0;
+               sample->time = event->fork.time;
+               sample->tid = event->fork.tid;
+               sample->pid = event->fork.pid;
+       }
+       print_sample_start(sample, thread, evsel);
+       perf_event__fprintf(event, stdout);
+
+       return 0;
+}
+static int process_exit_event(struct perf_tool *tool,
+                             union perf_event *event,
+                             struct perf_sample *sample,
+                             struct machine *machine)
+{
+       struct thread *thread;
+       struct perf_script *script = container_of(tool, struct perf_script, tool);
+       struct perf_session *session = script->session;
+       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+
+       thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
+       if (thread == NULL) {
+               pr_debug("problem processing EXIT event, skipping it.\n");
+               return -1;
+       }
+
+       if (!evsel->attr.sample_id_all) {
+               sample->cpu = 0;
+               sample->time = 0;
+               sample->tid = event->comm.tid;
+               sample->pid = event->comm.pid;
+       }
+       print_sample_start(sample, thread, evsel);
+       perf_event__fprintf(event, stdout);
+
+       if (perf_event__process_exit(tool, event, sample, machine) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int process_mmap_event(struct perf_tool *tool,
+                             union perf_event *event,
+                             struct perf_sample *sample,
+                             struct machine *machine)
+{
+       struct thread *thread;
+       struct perf_script *script = container_of(tool, struct perf_script, tool);
+       struct perf_session *session = script->session;
+       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+
+       if (perf_event__process_mmap(tool, event, sample, machine) < 0)
+               return -1;
+
+       thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid);
+       if (thread == NULL) {
+               pr_debug("problem processing MMAP event, skipping it.\n");
+               return -1;
+       }
+
+       if (!evsel->attr.sample_id_all) {
+               sample->cpu = 0;
+               sample->time = 0;
+               sample->tid = event->mmap.tid;
+               sample->pid = event->mmap.pid;
+       }
+       print_sample_start(sample, thread, evsel);
+       perf_event__fprintf(event, stdout);
+
+       return 0;
+}
+
+static int process_mmap2_event(struct perf_tool *tool,
+                             union perf_event *event,
+                             struct perf_sample *sample,
+                             struct machine *machine)
+{
+       struct thread *thread;
+       struct perf_script *script = container_of(tool, struct perf_script, tool);
+       struct perf_session *session = script->session;
+       struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+
+       if (perf_event__process_mmap2(tool, event, sample, machine) < 0)
+               return -1;
+
+       thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid);
+       if (thread == NULL) {
+               pr_debug("problem processing MMAP2 event, skipping it.\n");
+               return -1;
+       }
+
+       if (!evsel->attr.sample_id_all) {
+               sample->cpu = 0;
+               sample->time = 0;
+               sample->tid = event->mmap2.tid;
+               sample->pid = event->mmap2.pid;
+       }
+       print_sample_start(sample, thread, evsel);
+       perf_event__fprintf(event, stdout);
+
+       return 0;
+}
+
 static void sig_handler(int sig __maybe_unused)
 {
        session_done = 1;
@@ -590,6 +772,17 @@ static int __cmd_script(struct perf_script *script)
 
        signal(SIGINT, sig_handler);
 
+       /* override event processing functions */
+       if (script->show_task_events) {
+               script->tool.comm = process_comm_event;
+               script->tool.fork = process_fork_event;
+               script->tool.exit = process_exit_event;
+       }
+       if (script->show_mmap_events) {
+               script->tool.mmap = process_mmap_event;
+               script->tool.mmap2 = process_mmap2_event;
+       }
+
        ret = perf_session__process_events(script->session, &script->tool);
 
        if (debug_mode)
@@ -1352,6 +1545,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                    "display extended information from perf.data file"),
        OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
                    "Show the path of [kernel.kallsyms]"),
+       OPT_BOOLEAN('\0', "show-task-events", &script.show_task_events,
+                   "Show the fork/comm/exit events"),
+       OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events,
+                   "Show the mmap events"),
        OPT_END()
        };
        const char * const script_usage[] = {
index ee0d565f83e300f57d21d465a9f65180f5a74618..dab98b50c9feb800eac1652d4c66d2cf4c691c97 100644 (file)
@@ -138,6 +138,7 @@ static const char           *post_cmd                       = NULL;
 static bool                    sync_run                        = false;
 static unsigned int            interval                        = 0;
 static unsigned int            initial_delay                   = 0;
+static unsigned int            unit_width                      = 4; /* strlen("unit") */
 static bool                    forever                         = false;
 static struct timespec         ref_time;
 static struct cpu_map          *aggr_map;
@@ -461,17 +462,17 @@ static void print_interval(void)
        if (num_print_interval == 0 && !csv_output) {
                switch (aggr_mode) {
                case AGGR_SOCKET:
-                       fprintf(output, "#           time socket cpus             counts events\n");
+                       fprintf(output, "#           time socket cpus             counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_CORE:
-                       fprintf(output, "#           time core         cpus             counts events\n");
+                       fprintf(output, "#           time core         cpus             counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_NONE:
-                       fprintf(output, "#           time CPU                 counts events\n");
+                       fprintf(output, "#           time CPU                counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_GLOBAL:
                default:
-                       fprintf(output, "#           time             counts events\n");
+                       fprintf(output, "#           time             counts %*s events\n", unit_width, "unit");
                }
        }
 
@@ -516,6 +517,7 @@ static int __run_perf_stat(int argc, const char **argv)
        unsigned long long t0, t1;
        struct perf_evsel *counter;
        struct timespec ts;
+       size_t l;
        int status = 0;
        const bool forks = (argc > 0);
 
@@ -565,6 +567,10 @@ static int __run_perf_stat(int argc, const char **argv)
                        return -1;
                }
                counter->supported = true;
+
+               l = strlen(counter->unit);
+               if (l > unit_width)
+                       unit_width = l;
        }
 
        if (perf_evlist__apply_filters(evsel_list)) {
@@ -704,14 +710,25 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
 static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
 {
        double msecs = avg / 1e6;
-       const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s";
+       const char *fmt_v, *fmt_n;
        char name[25];
 
+       fmt_v = csv_output ? "%.6f%s" : "%18.6f%s";
+       fmt_n = csv_output ? "%s" : "%-25s";
+
        aggr_printout(evsel, cpu, nr);
 
        scnprintf(name, sizeof(name), "%s%s",
                  perf_evsel__name(evsel), csv_output ? "" : " (msec)");
-       fprintf(output, fmt, msecs, csv_sep, name);
+
+       fprintf(output, fmt_v, msecs, csv_sep);
+
+       if (csv_output)
+               fprintf(output, "%s%s", evsel->unit, csv_sep);
+       else
+               fprintf(output, "%-*s%s", unit_width, evsel->unit, csv_sep);
+
+       fprintf(output, fmt_n, name);
 
        if (evsel->cgrp)
                fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@@ -908,21 +925,31 @@ static void print_ll_cache_misses(int cpu,
 static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
 {
        double total, ratio = 0.0, total2;
+       double sc =  evsel->scale;
        const char *fmt;
 
-       if (csv_output)
-               fmt = "%.0f%s%s";
-       else if (big_num)
-               fmt = "%'18.0f%s%-25s";
-       else
-               fmt = "%18.0f%s%-25s";
+       if (csv_output) {
+               fmt = sc != 1.0 ?  "%.2f%s" : "%.0f%s";
+       } else {
+               if (big_num)
+                       fmt = sc != 1.0 ? "%'18.2f%s" : "%'18.0f%s";
+               else
+                       fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s";
+       }
 
        aggr_printout(evsel, cpu, nr);
 
        if (aggr_mode == AGGR_GLOBAL)
                cpu = 0;
 
-       fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel));
+       fprintf(output, fmt, avg, csv_sep);
+
+       if (evsel->unit)
+               fprintf(output, "%-*s%s",
+                       csv_output ? 0 : unit_width,
+                       evsel->unit, csv_sep);
+
+       fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel));
 
        if (evsel->cgrp)
                fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@@ -941,7 +968,10 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
 
                if (total && avg) {
                        ratio = total / avg;
-                       fprintf(output, "\n                                             #   %5.2f  stalled cycles per insn", ratio);
+                       fprintf(output, "\n");
+                       if (aggr_mode == AGGR_NONE)
+                               fprintf(output, "        ");
+                       fprintf(output, "                                                  #   %5.2f  stalled cycles per insn", ratio);
                }
 
        } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) &&
@@ -1061,6 +1091,7 @@ static void print_aggr(char *prefix)
 {
        struct perf_evsel *counter;
        int cpu, cpu2, s, s2, id, nr;
+       double uval;
        u64 ena, run, val;
 
        if (!(aggr_map || aggr_get_id))
@@ -1087,11 +1118,17 @@ static void print_aggr(char *prefix)
                        if (run == 0 || ena == 0) {
                                aggr_printout(counter, id, nr);
 
-                               fprintf(output, "%*s%s%*s",
+                               fprintf(output, "%*s%s",
                                        csv_output ? 0 : 18,
                                        counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
-                                       csv_sep,
-                                       csv_output ? 0 : -24,
+                                       csv_sep);
+
+                               fprintf(output, "%-*s%s",
+                                       csv_output ? 0 : unit_width,
+                                       counter->unit, csv_sep);
+
+                               fprintf(output, "%*s",
+                                       csv_output ? 0 : -25,
                                        perf_evsel__name(counter));
 
                                if (counter->cgrp)
@@ -1101,11 +1138,12 @@ static void print_aggr(char *prefix)
                                fputc('\n', output);
                                continue;
                        }
+                       uval = val * counter->scale;
 
                        if (nsec_counter(counter))
-                               nsec_printout(id, nr, counter, val);
+                               nsec_printout(id, nr, counter, uval);
                        else
-                               abs_printout(id, nr, counter, val);
+                               abs_printout(id, nr, counter, uval);
 
                        if (!csv_output) {
                                print_noise(counter, 1.0);
@@ -1128,16 +1166,21 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
        struct perf_stat *ps = counter->priv;
        double avg = avg_stats(&ps->res_stats[0]);
        int scaled = counter->counts->scaled;
+       double uval;
 
        if (prefix)
                fprintf(output, "%s", prefix);
 
        if (scaled == -1) {
-               fprintf(output, "%*s%s%*s",
+               fprintf(output, "%*s%s",
                        csv_output ? 0 : 18,
                        counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
-                       csv_sep,
-                       csv_output ? 0 : -24,
+                       csv_sep);
+               fprintf(output, "%-*s%s",
+                       csv_output ? 0 : unit_width,
+                       counter->unit, csv_sep);
+               fprintf(output, "%*s",
+                       csv_output ? 0 : -25,
                        perf_evsel__name(counter));
 
                if (counter->cgrp)
@@ -1147,10 +1190,12 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
                return;
        }
 
+       uval = avg * counter->scale;
+
        if (nsec_counter(counter))
-               nsec_printout(-1, 0, counter, avg);
+               nsec_printout(-1, 0, counter, uval);
        else
-               abs_printout(-1, 0, counter, avg);
+               abs_printout(-1, 0, counter, uval);
 
        print_noise(counter, avg);
 
@@ -1177,6 +1222,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
 static void print_counter(struct perf_evsel *counter, char *prefix)
 {
        u64 ena, run, val;
+       double uval;
        int cpu;
 
        for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
@@ -1188,14 +1234,20 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
                        fprintf(output, "%s", prefix);
 
                if (run == 0 || ena == 0) {
-                       fprintf(output, "CPU%*d%s%*s%s%*s",
+                       fprintf(output, "CPU%*d%s%*s%s",
                                csv_output ? 0 : -4,
                                perf_evsel__cpus(counter)->map[cpu], csv_sep,
                                csv_output ? 0 : 18,
                                counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
-                               csv_sep,
-                               csv_output ? 0 : -24,
-                               perf_evsel__name(counter));
+                               csv_sep);
+
+                               fprintf(output, "%-*s%s",
+                                       csv_output ? 0 : unit_width,
+                                       counter->unit, csv_sep);
+
+                               fprintf(output, "%*s",
+                                       csv_output ? 0 : -25,
+                                       perf_evsel__name(counter));
 
                        if (counter->cgrp)
                                fprintf(output, "%s%s",
@@ -1205,10 +1257,12 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
                        continue;
                }
 
+               uval = val * counter->scale;
+
                if (nsec_counter(counter))
-                       nsec_printout(cpu, 0, counter, val);
+                       nsec_printout(cpu, 0, counter, uval);
                else
-                       abs_printout(cpu, 0, counter, val);
+                       abs_printout(cpu, 0, counter, uval);
 
                if (!csv_output) {
                        print_noise(counter, 1.0);
index 41c9bde2fb67f1418faceaaf86475a163694bdeb..680632d7e26a1a372e8e918d92947cf8726ad166 100644 (file)
@@ -41,6 +41,7 @@
 #define SUPPORT_OLD_POWER_EVENTS 1
 #define PWR_EVENT_EXIT -1
 
+static int proc_num = 15;
 
 static unsigned int    numcpus;
 static u64             min_freq;       /* Lowest CPU frequency seen */
@@ -50,16 +51,12 @@ static u64          turbo_frequency;
 static u64             first_time, last_time;
 
 static bool            power_only;
+static bool            tasks_only;
+static bool            with_backtrace;
 
 
-struct per_pid;
 struct per_pidcomm;
-
 struct cpu_sample;
-struct power_event;
-struct wake_event;
-
-struct sample_wrapper;
 
 /*
  * Datastructure layout:
@@ -124,6 +121,7 @@ struct cpu_sample {
        u64 end_time;
        int type;
        int cpu;
+       const char *backtrace;
 };
 
 static struct per_pid *all_data;
@@ -145,12 +143,12 @@ struct wake_event {
        int waker;
        int wakee;
        u64 time;
+       const char *backtrace;
 };
 
 static struct power_event    *power_events;
 static struct wake_event     *wake_events;
 
-struct process_filter;
 struct process_filter {
        char                    *name;
        int                     pid;
@@ -229,7 +227,8 @@ static void pid_exit(int pid, u64 timestamp)
 }
 
 static void
-pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
+pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end,
+              const char *backtrace)
 {
        struct per_pid *p;
        struct per_pidcomm *c;
@@ -252,6 +251,7 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
        sample->type = type;
        sample->next = c->samples;
        sample->cpu = cpu;
+       sample->backtrace = backtrace;
        c->samples = sample;
 
        if (sample->type == TYPE_RUNNING && end > start && start > 0) {
@@ -299,50 +299,10 @@ static int process_exit_event(struct perf_tool *tool __maybe_unused,
        return 0;
 }
 
-struct trace_entry {
-       unsigned short          type;
-       unsigned char           flags;
-       unsigned char           preempt_count;
-       int                     pid;
-       int                     lock_depth;
-};
-
 #ifdef SUPPORT_OLD_POWER_EVENTS
 static int use_old_power_events;
-struct power_entry_old {
-       struct trace_entry te;
-       u64     type;
-       u64     value;
-       u64     cpu_id;
-};
 #endif
 
-struct power_processor_entry {
-       struct trace_entry te;
-       u32     state;
-       u32     cpu_id;
-};
-
-#define TASK_COMM_LEN 16
-struct wakeup_entry {
-       struct trace_entry te;
-       char comm[TASK_COMM_LEN];
-       int   pid;
-       int   prio;
-       int   success;
-};
-
-struct sched_switch {
-       struct trace_entry te;
-       char prev_comm[TASK_COMM_LEN];
-       int  prev_pid;
-       int  prev_prio;
-       long prev_state; /* Arjan weeps. */
-       char next_comm[TASK_COMM_LEN];
-       int  next_pid;
-       int  next_prio;
-};
-
 static void c_state_start(int cpu, u64 timestamp, int state)
 {
        cpus_cstate_start_times[cpu] = timestamp;
@@ -402,23 +362,23 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
                        turbo_frequency = max_freq;
 }
 
-static void
-sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
+static void sched_wakeup(int cpu, u64 timestamp, int waker, int wakee,
+                        u8 flags, const char *backtrace)
 {
        struct per_pid *p;
-       struct wakeup_entry *wake = (void *)te;
        struct wake_event *we = zalloc(sizeof(*we));
 
        if (!we)
                return;
 
        we->time = timestamp;
-       we->waker = pid;
+       we->waker = waker;
+       we->backtrace = backtrace;
 
-       if ((te->flags & TRACE_FLAG_HARDIRQ) || (te->flags & TRACE_FLAG_SOFTIRQ))
+       if ((flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ))
                we->waker = -1;
 
-       we->wakee = wake->pid;
+       we->wakee = wakee;
        we->next = wake_events;
        wake_events = we;
        p = find_create_pid(we->wakee);
@@ -428,27 +388,31 @@ sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
                p->current->state = TYPE_WAITING;
        }
        if (p && p->current && p->current->state == TYPE_BLOCKED) {
-               pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);
+               pid_put_sample(p->pid, p->current->state, cpu,
+                              p->current->state_since, timestamp, NULL);
                p->current->state_since = timestamp;
                p->current->state = TYPE_WAITING;
        }
 }
 
-static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
+static void sched_switch(int cpu, u64 timestamp, int prev_pid, int next_pid,
+                        u64 prev_state, const char *backtrace)
 {
        struct per_pid *p = NULL, *prev_p;
-       struct sched_switch *sw = (void *)te;
 
+       prev_p = find_create_pid(prev_pid);
 
-       prev_p = find_create_pid(sw->prev_pid);
-
-       p = find_create_pid(sw->next_pid);
+       p = find_create_pid(next_pid);
 
        if (prev_p->current && prev_p->current->state != TYPE_NONE)
-               pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);
+               pid_put_sample(prev_pid, TYPE_RUNNING, cpu,
+                              prev_p->current->state_since, timestamp,
+                              backtrace);
        if (p && p->current) {
                if (p->current->state != TYPE_NONE)
-                       pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
+                       pid_put_sample(next_pid, p->current->state, cpu,
+                                      p->current->state_since, timestamp,
+                                      backtrace);
 
                p->current->state_since = timestamp;
                p->current->state = TYPE_RUNNING;
@@ -457,18 +421,97 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
        if (prev_p->current) {
                prev_p->current->state = TYPE_NONE;
                prev_p->current->state_since = timestamp;
-               if (sw->prev_state & 2)
+               if (prev_state & 2)
                        prev_p->current->state = TYPE_BLOCKED;
-               if (sw->prev_state == 0)
+               if (prev_state == 0)
                        prev_p->current->state = TYPE_WAITING;
        }
 }
 
+static const char *cat_backtrace(union perf_event *event,
+                                struct perf_sample *sample,
+                                struct machine *machine)
+{
+       struct addr_location al;
+       unsigned int i;
+       char *p = NULL;
+       size_t p_len;
+       u8 cpumode = PERF_RECORD_MISC_USER;
+       struct addr_location tal;
+       struct ip_callchain *chain = sample->callchain;
+       FILE *f = open_memstream(&p, &p_len);
+
+       if (!f) {
+               perror("open_memstream error");
+               return NULL;
+       }
+
+       if (!chain)
+               goto exit;
+
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
+               fprintf(stderr, "problem processing %d event, skipping it.\n",
+                       event->header.type);
+               goto exit;
+       }
+
+       for (i = 0; i < chain->nr; i++) {
+               u64 ip;
+
+               if (callchain_param.order == ORDER_CALLEE)
+                       ip = chain->ips[i];
+               else
+                       ip = chain->ips[chain->nr - i - 1];
+
+               if (ip >= PERF_CONTEXT_MAX) {
+                       switch (ip) {
+                       case PERF_CONTEXT_HV:
+                               cpumode = PERF_RECORD_MISC_HYPERVISOR;
+                               break;
+                       case PERF_CONTEXT_KERNEL:
+                               cpumode = PERF_RECORD_MISC_KERNEL;
+                               break;
+                       case PERF_CONTEXT_USER:
+                               cpumode = PERF_RECORD_MISC_USER;
+                               break;
+                       default:
+                               pr_debug("invalid callchain context: "
+                                        "%"PRId64"\n", (s64) ip);
+
+                               /*
+                                * It seems the callchain is corrupted.
+                                * Discard all.
+                                */
+                               free(p);
+                               p = NULL;
+                               goto exit;
+                       }
+                       continue;
+               }
+
+               tal.filtered = false;
+               thread__find_addr_location(al.thread, machine, cpumode,
+                                          MAP__FUNCTION, ip, &tal);
+
+               if (tal.sym)
+                       fprintf(f, "..... %016" PRIx64 " %s\n", ip,
+                               tal.sym->name);
+               else
+                       fprintf(f, "..... %016" PRIx64 "\n", ip);
+       }
+
+exit:
+       fclose(f);
+
+       return p;
+}
+
 typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
-                                 struct perf_sample *sample);
+                                 struct perf_sample *sample,
+                                 const char *backtrace);
 
 static int process_sample_event(struct perf_tool *tool __maybe_unused,
-                               union perf_event *event __maybe_unused,
+                               union perf_event *event,
                                struct perf_sample *sample,
                                struct perf_evsel *evsel,
                                struct machine *machine __maybe_unused)
@@ -485,81 +528,97 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
 
        if (evsel->handler != NULL) {
                tracepoint_handler f = evsel->handler;
-               return f(evsel, sample);
+               return f(evsel, sample, cat_backtrace(event, sample, machine));
        }
 
        return 0;
 }
 
 static int
-process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused,
-                       struct perf_sample *sample)
+process_sample_cpu_idle(struct perf_evsel *evsel,
+                       struct perf_sample *sample,
+                       const char *backtrace __maybe_unused)
 {
-       struct power_processor_entry *ppe = sample->raw_data;
+       u32 state = perf_evsel__intval(evsel, sample, "state");
+       u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
 
-       if (ppe->state == (u32) PWR_EVENT_EXIT)
-               c_state_end(ppe->cpu_id, sample->time);
+       if (state == (u32)PWR_EVENT_EXIT)
+               c_state_end(cpu_id, sample->time);
        else
-               c_state_start(ppe->cpu_id, sample->time, ppe->state);
+               c_state_start(cpu_id, sample->time, state);
        return 0;
 }
 
 static int
-process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused,
-                            struct perf_sample *sample)
+process_sample_cpu_frequency(struct perf_evsel *evsel,
+                            struct perf_sample *sample,
+                            const char *backtrace __maybe_unused)
 {
-       struct power_processor_entry *ppe = sample->raw_data;
+       u32 state = perf_evsel__intval(evsel, sample, "state");
+       u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
 
-       p_state_change(ppe->cpu_id, sample->time, ppe->state);
+       p_state_change(cpu_id, sample->time, state);
        return 0;
 }
 
 static int
-process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused,
-                           struct perf_sample *sample)
+process_sample_sched_wakeup(struct perf_evsel *evsel,
+                           struct perf_sample *sample,
+                           const char *backtrace)
 {
-       struct trace_entry *te = sample->raw_data;
+       u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
+       int waker = perf_evsel__intval(evsel, sample, "common_pid");
+       int wakee = perf_evsel__intval(evsel, sample, "pid");
 
-       sched_wakeup(sample->cpu, sample->time, sample->pid, te);
+       sched_wakeup(sample->cpu, sample->time, waker, wakee, flags, backtrace);
        return 0;
 }
 
 static int
-process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused,
-                           struct perf_sample *sample)
+process_sample_sched_switch(struct perf_evsel *evsel,
+                           struct perf_sample *sample,
+                           const char *backtrace)
 {
-       struct trace_entry *te = sample->raw_data;
+       int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
+       int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+       u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
 
-       sched_switch(sample->cpu, sample->time, te);
+       sched_switch(sample->cpu, sample->time, prev_pid, next_pid, prev_state,
+                    backtrace);
        return 0;
 }
 
 #ifdef SUPPORT_OLD_POWER_EVENTS
 static int
-process_sample_power_start(struct perf_evsel *evsel __maybe_unused,
-                          struct perf_sample *sample)
+process_sample_power_start(struct perf_evsel *evsel,
+                          struct perf_sample *sample,
+                          const char *backtrace __maybe_unused)
 {
-       struct power_entry_old *peo = sample->raw_data;
+       u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
+       u64 value = perf_evsel__intval(evsel, sample, "value");
 
-       c_state_start(peo->cpu_id, sample->time, peo->value);
+       c_state_start(cpu_id, sample->time, value);
        return 0;
 }
 
 static int
 process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
-                        struct perf_sample *sample)
+                        struct perf_sample *sample,
+                        const char *backtrace __maybe_unused)
 {
        c_state_end(sample->cpu, sample->time);
        return 0;
 }
 
 static int
-process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused,
-                              struct perf_sample *sample)
+process_sample_power_frequency(struct perf_evsel *evsel,
+                              struct perf_sample *sample,
+                              const char *backtrace __maybe_unused)
 {
-       struct power_entry_old *peo = sample->raw_data;
+       u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
+       u64 value = perf_evsel__intval(evsel, sample, "value");
 
-       p_state_change(peo->cpu_id, sample->time, peo->value);
+       p_state_change(cpu_id, sample->time, value);
        return 0;
 }
 #endif /* SUPPORT_OLD_POWER_EVENTS */
@@ -739,11 +798,12 @@ static void draw_wakeups(void)
                }
 
                if (we->waker == -1)
-                       svg_interrupt(we->time, to);
+                       svg_interrupt(we->time, to, we->backtrace);
                else if (from && to && abs(from - to) == 1)
-                       svg_wakeline(we->time, from, to);
+                       svg_wakeline(we->time, from, to, we->backtrace);
                else
-                       svg_partial_wakeline(we->time, from, task_from, to, task_to);
+                       svg_partial_wakeline(we->time, from, task_from, to,
+                                            task_to, we->backtrace);
                we = we->next;
 
                free(task_from);
@@ -796,11 +856,20 @@ static void draw_process_bars(void)
                        sample = c->samples;
                        while (sample) {
                                if (sample->type == TYPE_RUNNING)
-                                       svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);
+                                       svg_running(Y, sample->cpu,
+                                                   sample->start_time,
+                                                   sample->end_time,
+                                                   sample->backtrace);
                                if (sample->type == TYPE_BLOCKED)
-                                       svg_box(Y, sample->start_time, sample->end_time, "blocked");
+                                       svg_blocked(Y, sample->cpu,
+                                                   sample->start_time,
+                                                   sample->end_time,
+                                                   sample->backtrace);
                                if (sample->type == TYPE_WAITING)
-                                       svg_waiting(Y, sample->start_time, sample->end_time);
+                                       svg_waiting(Y, sample->cpu,
+                                                   sample->start_time,
+                                                   sample->end_time,
+                                                   sample->backtrace);
                                sample = sample->next;
                        }
 
@@ -911,7 +980,7 @@ static int determine_display_tasks(u64 threshold)
                /* no exit marker, task kept running to the end */
                if (p->end_time == 0)
                        p->end_time = last_time;
-               if (p->total_time >= threshold && !power_only)
+               if (p->total_time >= threshold)
                        p->display = 1;
 
                c = p->all;
@@ -922,7 +991,7 @@ static int determine_display_tasks(u64 threshold)
                        if (c->start_time == 1)
                                c->start_time = first_time;
 
-                       if (c->total_time >= threshold && !power_only) {
+                       if (c->total_time >= threshold) {
                                c->display = 1;
                                count++;
                        }
@@ -945,15 +1014,19 @@ static void write_svg_file(const char *filename)
 {
        u64 i;
        int count;
+       int thresh = TIME_THRESH;
 
        numcpus++;
 
+       if (power_only)
+               proc_num = 0;
 
-       count = determine_display_tasks(TIME_THRESH);
-
-       /* We'd like to show at least 15 tasks; be less picky if we have fewer */
-       if (count < 15)
-               count = determine_display_tasks(TIME_THRESH / 10);
+       /* We'd like to show at least proc_num tasks;
+        * be less picky if we have fewer */
+       do {
+               count = determine_display_tasks(thresh);
+               thresh /= 10;
+       } while (!process_filter && thresh && count < proc_num);
 
        open_svg(filename, numcpus, count, first_time, last_time);
 
@@ -964,9 +1037,12 @@ static void write_svg_file(const char *filename)
                svg_cpu_box(i, max_freq, turbo_frequency);
 
        draw_cpu_usage();
-       draw_process_bars();
-       draw_c_p_states();
-       draw_wakeups();
+       if (proc_num)
+               draw_process_bars();
+       if (!tasks_only)
+               draw_c_p_states();
+       if (proc_num)
+               draw_wakeups();
 
        svg_close();
 }
@@ -1031,50 +1107,92 @@ out_delete:
 
 static int __cmd_record(int argc, const char **argv)
 {
-#ifdef SUPPORT_OLD_POWER_EVENTS
-       const char * const record_old_args[] = {
+       unsigned int rec_argc, i, j;
+       const char **rec_argv;
+       const char **p;
+       unsigned int record_elems;
+
+       const char * const common_args[] = {
                "record", "-a", "-R", "-c", "1",
+       };
+       unsigned int common_args_nr = ARRAY_SIZE(common_args);
+
+       const char * const backtrace_args[] = {
+               "-g",
+       };
+       unsigned int backtrace_args_no = ARRAY_SIZE(backtrace_args);
+
+       const char * const power_args[] = {
+               "-e", "power:cpu_frequency",
+               "-e", "power:cpu_idle",
+       };
+       unsigned int power_args_nr = ARRAY_SIZE(power_args);
+
+       const char * const old_power_args[] = {
+#ifdef SUPPORT_OLD_POWER_EVENTS
                "-e", "power:power_start",
                "-e", "power:power_end",
                "-e", "power:power_frequency",
-               "-e", "sched:sched_wakeup",
-               "-e", "sched:sched_switch",
-       };
 #endif
-       const char * const record_new_args[] = {
-               "record", "-a", "-R", "-c", "1",
-               "-e", "power:cpu_frequency",
-               "-e", "power:cpu_idle",
+       };
+       unsigned int old_power_args_nr = ARRAY_SIZE(old_power_args);
+
+       const char * const tasks_args[] = {
                "-e", "sched:sched_wakeup",
                "-e", "sched:sched_switch",
        };
-       unsigned int rec_argc, i, j;
-       const char **rec_argv;
-       const char * const *record_args = record_new_args;
-       unsigned int record_elems = ARRAY_SIZE(record_new_args);
+       unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
 
 #ifdef SUPPORT_OLD_POWER_EVENTS
        if (!is_valid_tracepoint("power:cpu_idle") &&
            is_valid_tracepoint("power:power_start")) {
                use_old_power_events = 1;
-               record_args = record_old_args;
-               record_elems = ARRAY_SIZE(record_old_args);
+               power_args_nr = 0;
+       } else {
+               old_power_args_nr = 0;
        }
 #endif
 
-       rec_argc = record_elems + argc - 1;
+       if (power_only)
+               tasks_args_nr = 0;
+
+       if (tasks_only) {
+               power_args_nr = 0;
+               old_power_args_nr = 0;
+       }
+
+       if (!with_backtrace)
+               backtrace_args_no = 0;
+
+       record_elems = common_args_nr + tasks_args_nr +
+               power_args_nr + old_power_args_nr + backtrace_args_no;
+
+       rec_argc = record_elems + argc;
        rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
        if (rec_argv == NULL)
                return -ENOMEM;
 
-       for (i = 0; i < record_elems; i++)
-               rec_argv[i] = strdup(record_args[i]);
+       p = rec_argv;
+       for (i = 0; i < common_args_nr; i++)
+               *p++ = strdup(common_args[i]);
 
-       for (j = 1; j < (unsigned int)argc; j++, i++)
-               rec_argv[i] = argv[j];
+       for (i = 0; i < backtrace_args_no; i++)
+               *p++ = strdup(backtrace_args[i]);
 
-       return cmd_record(i, rec_argv, NULL);
+       for (i = 0; i < tasks_args_nr; i++)
+               *p++ = strdup(tasks_args[i]);
+
+       for (i = 0; i < power_args_nr; i++)
+               *p++ = strdup(power_args[i]);
+
+       for (i = 0; i < old_power_args_nr; i++)
+               *p++ = strdup(old_power_args[i]);
+
+       for (j = 1; j < (unsigned int)argc; j++)
+               *p++ = argv[j];
+
+       return cmd_record(rec_argc, rec_argv, NULL);
 }
 
 static int
@@ -1090,16 +1208,20 @@ int cmd_timechart(int argc, const char **argv,
                  const char *prefix __maybe_unused)
 {
        const char *output_name = "output.svg";
-       const struct option options[] = {
+       const struct option timechart_options[] = {
        OPT_STRING('i', "input", &input_name, "file", "input file name"),
        OPT_STRING('o', "output", &output_name, "file", "output file name"),
        OPT_INTEGER('w', "width", &svg_page_width, "page width"),
        OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
+       OPT_BOOLEAN('T', "tasks-only", &tasks_only,
+                   "output processes data only"),
        OPT_CALLBACK('p', "process", NULL, "process",
                      "process selector. Pass a pid or process name.",
                       parse_process),
        OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
                    "Look for files with symbols relative to this directory"),
+       OPT_INTEGER('n', "proc-num", &proc_num,
+                   "min. number of tasks to print"),
        OPT_END()
        };
        const char * const timechart_usage[] = {
@@ -1107,15 +1229,39 @@ int cmd_timechart(int argc, const char **argv,
                NULL
        };
 
-       argc = parse_options(argc, argv, options, timechart_usage,
+       const struct option record_options[] = {
+       OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"),
+       OPT_BOOLEAN('T', "tasks-only", &tasks_only,
+                   "output processes data only"),
+       OPT_BOOLEAN('g', "callchain", &with_backtrace, "record callchain"),
+       OPT_END()
+       };
+       const char * const record_usage[] = {
+               "perf timechart record [<options>]",
+               NULL
+       };
+       argc = parse_options(argc, argv, timechart_options, timechart_usage,
                        PARSE_OPT_STOP_AT_NON_OPTION);
 
+       if (power_only && tasks_only) {
+               pr_err("-P and -T options cannot be used at the same time.\n");
+               return -1;
+       }
+
        symbol__init();
 
-       if (argc && !strncmp(argv[0], "rec", 3))
+       if (argc && !strncmp(argv[0], "rec", 3)) {
+               argc = parse_options(argc, argv, record_options, record_usage,
+                                    PARSE_OPT_STOP_AT_NON_OPTION);
+
+               if (power_only && tasks_only) {
+                       pr_err("-P and -T options cannot be used at the same time.\n");
+                       return -1;
+               }
+
                return __cmd_record(argc, argv);
-       else if (argc)
-               usage_with_options(timechart_usage, options);
+       else if (argc)
+               usage_with_options(timechart_usage, timechart_options);
 
        setup_pager();
 
index b8f8e29db332df707887284d539b6ad6f3d5840f..03d37a76c6122d6328034322dc9c3f99830b623d 100644 (file)
@@ -634,26 +634,9 @@ repeat:
        return NULL;
 }
 
-/* Tag samples to be skipped. */
-static const char *skip_symbols[] = {
-       "intel_idle",
-       "default_idle",
-       "native_safe_halt",
-       "cpu_idle",
-       "enter_idle",
-       "exit_idle",
-       "mwait_idle",
-       "mwait_idle_with_hints",
-       "poll_idle",
-       "ppc64_runlatch_off",
-       "pseries_dedicated_idle_sleep",
-       NULL
-};
-
 static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
 {
        const char *name = sym->name;
-       int i;
 
        /*
         * ppc64 uses function descriptors and appends a '.' to the
@@ -671,12 +654,8 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
            strstr(name, "_text_end"))
                return 1;
 
-       for (i = 0; skip_symbols[i]; i++) {
-               if (!strcmp(skip_symbols[i], name)) {
-                       sym->ignore = true;
-                       break;
-               }
-       }
+       if (symbol__is_idle(sym))
+               sym->ignore = true;
 
        return 0;
 }
@@ -1084,7 +1063,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                            "dump the symbol table used for profiling"),
        OPT_INTEGER('f', "count-filter", &top.count_filter,
                    "only display functions with more events than this"),
-       OPT_BOOLEAN('g', "group", &opts->group,
+       OPT_BOOLEAN(0, "group", &opts->group,
                            "put the counters into a counter group"),
        OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit,
                    "child tasks do not inherit counters"),
@@ -1105,7 +1084,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                   " abort, in_tx, transaction"),
        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
                    "Show a column with the number of samples"),
-       OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts,
+       OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
                           NULL, "enables call-graph recording",
                           &callchain_opt),
        OPT_CALLBACK(0, "call-graph", &top.record_opts,
@@ -1172,7 +1151,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 +1159,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 6b230af940e2ab0c136098ef49b21522b6c165b6..e9f345e2551a18be6e5a17af51b8547f89c5ddbb 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);
@@ -2158,7 +2158,6 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
        size_t printed = data->printed;
        struct trace *trace = data->trace;
        struct thread_trace *ttrace = thread->priv;
-       const char *color;
        double ratio;
 
        if (ttrace == NULL)
@@ -2166,17 +2165,9 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
 
        ratio = (double)ttrace->nr_events / trace->nr_events * 100.0;
 
-       color = PERF_COLOR_NORMAL;
-       if (ratio > 50.0)
-               color = PERF_COLOR_RED;
-       else if (ratio > 25.0)
-               color = PERF_COLOR_GREEN;
-       else if (ratio > 5.0)
-               color = PERF_COLOR_YELLOW;
-
-       printed += color_fprintf(fp, color, " %s (%d), ", thread__comm_str(thread), thread->tid);
+       printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid);
        printed += fprintf(fp, "%lu events, ", ttrace->nr_events);
-       printed += color_fprintf(fp, color, "%.1f%%", ratio);
+       printed += fprintf(fp, "%.1f%%", ratio);
        printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms);
        printed += thread__dump_stats(ttrace, trace, fp);
 
index f5905f2b197d81d7cb2c8e0ee5a3f2cc257d5f20..f7d11a811c743e6e2836270f4579124007140d32 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 e8e195f49a4ee7695a80b2a4d535fb35a5aac504..87e790017c6988b856e3b4e8b47f572a9d8beea9 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 799865b607721e247b847711cf656ef375ada341..59e7a705e146d4eb8ea029ebf74878411f8e3898 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
similarity index 65%
rename from tools/perf/bash_completion
rename to tools/perf/perf-completion.sh
index 62e157db2e2b01e9400f9348d671fd9ba7267104..49494882d9bb8c1a65417589b20d691b08f57538 100644 (file)
@@ -1,4 +1,4 @@
-# perf completion
+# perf bash and zsh completion
 
 # Taken from git.git's completion script.
 __my_reassemble_comp_words_by_ref()
@@ -89,37 +89,113 @@ __ltrim_colon_completions()
        fi
 }
 
-type perf &>/dev/null &&
-_perf()
+__perfcomp ()
 {
-       local cur words cword prev cmd
+       COMPREPLY=( $( compgen -W "$1" -- "$2" ) )
+}
 
-       COMPREPLY=()
-       _get_comp_words_by_ref -n =: cur words cword prev
+__perfcomp_colon ()
+{
+       __perfcomp "$1" "$2"
+       __ltrim_colon_completions $cur
+}
+
+__perf_main ()
+{
+       local cmd
 
        cmd=${words[0]}
+       COMPREPLY=()
 
        # List perf subcommands or long options
        if [ $cword -eq 1 ]; then
                if [[ $cur == --* ]]; then
-                       COMPREPLY=( $( compgen -W '--help --version \
+                       __perfcomp '--help --version \
                        --exec-path --html-path --paginate --no-pager \
-                       --perf-dir --work-tree --debugfs-dir' -- "$cur" ) )
+                       --perf-dir --work-tree --debugfs-dir' -- "$cur"
                else
                        cmds=$($cmd --list-cmds)
-                       COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
+                       __perfcomp "$cmds" "$cur"
                fi
        # List possible events for -e option
        elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
                evts=$($cmd list --raw-dump)
-               COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) )
-               __ltrim_colon_completions $cur
+               __perfcomp_colon "$evts" "$cur"
        # List long option names
        elif [[ $cur == --* ]];  then
                subcmd=${words[1]}
                opts=$($cmd $subcmd --list-opts)
-               COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) )
+               __perfcomp "$opts" "$cur"
        fi
+}
+
+if [[ -n ${ZSH_VERSION-} ]]; then
+       autoload -U +X compinit && compinit
+
+       __perfcomp ()
+       {
+               emulate -L zsh
+
+               local c IFS=$' \t\n'
+               local -a array
+
+               for c in ${=1}; do
+                       case $c in
+                       --*=*|*.) ;;
+                       *) c="$c " ;;
+                       esac
+                       array[${#array[@]}+1]="$c"
+               done
+
+               compset -P '*[=:]'
+               compadd -Q -S '' -a -- array && _ret=0
+       }
+
+       __perfcomp_colon ()
+       {
+               emulate -L zsh
+
+               local cur_="${2-$cur}"
+               local c IFS=$' \t\n'
+               local -a array
+
+               if [[ "$cur_" == *:* ]]; then
+                       local colon_word=${cur_%"${cur_##*:}"}
+               fi
+
+               for c in ${=1}; do
+                       case $c in
+                       --*=*|*.) ;;
+                       *) c="$c " ;;
+                       esac
+                       array[$#array+1]=${c#"$colon_word"}
+               done
+
+               compset -P '*[=:]'
+               compadd -Q -S '' -a -- array && _ret=0
+       }
+
+       _perf ()
+       {
+               local _ret=1 cur cword prev
+               cur=${words[CURRENT]}
+               prev=${words[CURRENT-1]}
+               let cword=CURRENT-1
+               emulate ksh -c __perf_main
+               let _ret && _default && _ret=0
+               return _ret
+       }
+
+       compdef _perf perf
+       return
+fi
+
+type perf &>/dev/null &&
+_perf()
+{
+       local cur words cword prev
+       _get_comp_words_by_ref -n =: cur words cword prev
+       __perf_main
 } &&
 
 complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \
index b079304bd53daa87ca61a428f4611046741f58e6..b23fed5275149adfb1b5f826730341ef6d63f8be 100644 (file)
@@ -254,6 +254,7 @@ struct perf_record_opts {
        bool         inherit_stat;
        bool         no_delay;
        bool         no_inherit;
+       bool         no_inherit_set;
        bool         no_samples;
        bool         raw_samples;
        bool         sample_address;
index 9079a25cd643cdabaf570d19131e4cc0e4fef7f6..44edcb2edcd50b7c4696477e88f6e12176555303 100644 (file)
@@ -3,5 +3,5 @@ command = record
 args    = -i kill >/dev/null 2>&1
 
 [event:base-record]
-sample_type=259
+sample_type=263
 inherit=0
index ef671cd41bb3ef554478db74cecc61c6df28afca..3cbd104960871705778f37450f0e33d117430f07 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 bbc782e364b0831f04b12ce417b22ae6b5948b16..cbaa7af45513660d0fda1d1b6cbea8fa0a39b2ec 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 16848bb4c41842355be8c7a36d2df574a9c90214..a440e03cd8c235402b626526b41676b3ad95488b 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 6e3a846aed0ea99ac2958886a64a512359394cea..c77814bf01e1a7f89857ba8742ce7ec7cbcfaf5e 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
@@ -730,8 +732,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
        if (thread == NULL)
                return -1;
 
-       if (symbol_conf.comm_list &&
-           !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread)))
+       if (thread__is_filtered(thread))
                goto out_filtered;
 
        dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
index dc6fa3fbb180c69bf7a271450f11c52b45203880..76fa76431329f9b252d15e1b49b43e18d05c6c0a 100644 (file)
@@ -819,7 +819,11 @@ 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->default_per_cpu)
+               evlist->cpus = target->per_thread ?
+                                       cpu_map__dummy_new() :
+                                       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 +1152,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 18f7c188ff632816376c26b9f5a3594df4571fac..b5fe7f9b2e15091f5d38db1044159177d9c15f27 100644 (file)
@@ -162,6 +162,8 @@ void perf_evsel__init(struct perf_evsel *evsel,
        evsel->idx         = idx;
        evsel->attr        = *attr;
        evsel->leader      = evsel;
+       evsel->unit        = "";
+       evsel->scale       = 1.0;
        INIT_LIST_HEAD(&evsel->node);
        hists__init(&evsel->hists);
        evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
@@ -572,6 +574,7 @@ void perf_evsel__config(struct perf_evsel *evsel,
        struct perf_evsel *leader = evsel->leader;
        struct perf_event_attr *attr = &evsel->attr;
        int track = !evsel->idx; /* only the first counter needs these */
+       bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;
 
        attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
        attr->inherit       = !opts->no_inherit;
@@ -653,7 +656,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) || per_cpu))
                perf_evsel__set_sample_bit(evsel, TIME);
 
        if (opts->raw_samples) {
index f5029653dcd74f590aa2692c247cc3cefbd9e977..8120eeb86ac16c4c5b86c931785233e85ea4ac16 100644 (file)
@@ -68,6 +68,8 @@ struct perf_evsel {
        u32                     ids;
        struct hists            hists;
        char                    *name;
+       double                  scale;
+       const char              *unit;
        struct event_format     *tp_format;
        union {
                void            *priv;
@@ -138,6 +140,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
 int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
                                            char *bf, size_t size);
 const char *perf_evsel__name(struct perf_evsel *evsel);
+
 const char *perf_evsel__group_name(struct perf_evsel *evsel);
 int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
 
@@ -279,6 +282,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 369c03648f8846d3da12bf96a938f6e76485fbed..1cd035708931144dccbfdb68f4c1024903e45631 100644 (file)
@@ -2078,8 +2078,10 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,
                if (evsel->idx == (int) desc[i].leader_idx) {
                        evsel->leader = evsel;
                        /* {anon_group} is a dummy name */
-                       if (strcmp(desc[i].name, "{anon_group}"))
+                       if (strcmp(desc[i].name, "{anon_group}")) {
                                evsel->group_name = desc[i].name;
+                               desc[i].name = NULL;
+                       }
                        evsel->nr_members = desc[i].nr_members;
 
                        if (i >= nr_groups || nr > 0) {
@@ -2105,7 +2107,7 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused,
 
        ret = 0;
 out_free:
-       while ((int) --i >= 0)
+       for (i = 0; i < nr_groups; i++)
                free(desc[i].name);
        free(desc);
 
index 0393912d80336d641f055148a3c593a494597be4..84cdb072ac83975670c477fa180980ae61425bda 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 6de6f89c2a6175c96570055f57ccbb83cc8c58b5..969cb8f0d88d0c77152fec5b187b53c21cfd44ef 100644 (file)
@@ -269,9 +269,10 @@ const char *event_type(int type)
 
 
 
-static int __add_event(struct list_head *list, int *idx,
-                      struct perf_event_attr *attr,
-                      char *name, struct cpu_map *cpus)
+static struct perf_evsel *
+__add_event(struct list_head *list, int *idx,
+           struct perf_event_attr *attr,
+           char *name, struct cpu_map *cpus)
 {
        struct perf_evsel *evsel;
 
@@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx,
 
        evsel = perf_evsel__new_idx(attr, (*idx)++);
        if (!evsel)
-               return -ENOMEM;
+               return NULL;
 
        evsel->cpus = cpus;
        if (name)
                evsel->name = strdup(name);
        list_add_tail(&evsel->node, list);
-       return 0;
+       return evsel;
 }
 
 static int add_event(struct list_head *list, int *idx,
                     struct perf_event_attr *attr, char *name)
 {
-       return __add_event(list, idx, attr, name, NULL);
+       return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;
 }
 
 static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
@@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
 {
        struct perf_event_attr attr;
        struct perf_pmu *pmu;
+       struct perf_evsel *evsel;
+       char *unit;
+       double scale;
 
        pmu = perf_pmu__find(name);
        if (!pmu)
@@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
 
        memset(&attr, 0, sizeof(attr));
 
-       if (perf_pmu__check_alias(pmu, head_config))
+       if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
                return -EINVAL;
 
        /*
@@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
        if (perf_pmu__config(pmu, &attr, head_config))
                return -EINVAL;
 
-       return __add_event(list, idx, &attr, pmu_event_name(head_config),
-                          pmu->cpus);
+       evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
+                           pmu->cpus);
+       if (evsel) {
+               evsel->unit = unit;
+               evsel->scale = scale;
+       }
+
+       return evsel ? 0 : -ENOMEM;
 }
 
 int parse_events__modifier_group(struct list_head *list,
index 31f404a032a90499e5d366123f3185972edf3b49..d22e3f8017dc429a67458bc18457a6de7cd9f778 100644 (file)
@@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p,
 
        case OPTION_BOOLEAN:
                *(bool *)opt->value = unset ? false : true;
+               if (opt->set)
+                       *(bool *)opt->set = true;
                return 0;
 
        case OPTION_INCR:
@@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        return 0;
                }
                if (!rest) {
+                       if (!prefixcmp(options->long_name, "no-")) {
+                               /*
+                                * The long name itself starts with "no-", so
+                                * accept the option without "no-" so that users
+                                * do not have to enter "no-no-" to get the
+                                * negation.
+                                */
+                               rest = skip_prefix(arg, options->long_name + 3);
+                               if (rest) {
+                                       flags |= OPT_UNSET;
+                                       goto match;
+                               }
+                               /* Abbreviated case */
+                               if (!prefixcmp(options->long_name + 3, arg)) {
+                                       flags |= OPT_UNSET;
+                                       goto is_abbreviated;
+                               }
+                       }
                        /* abbreviated? */
                        if (!strncmp(options->long_name, arg, arg_end - arg)) {
 is_abbreviated:
@@ -259,6 +279,7 @@ is_abbreviated:
                        if (!rest)
                                continue;
                }
+match:
                if (*rest) {
                        if (*rest != '=')
                                continue;
index b0241e28eaf7dd7b81469e22416e8f1e16e26c63..cbf0149cf221783aea2aeafd8643900b80e6765d 100644 (file)
@@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  *   OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
  *   the value when met.
  *   CALLBACKS can use it like they want.
+ *
+ * `set`::
+ *   whether an option was set by the user
  */
 struct option {
        enum parse_opt_type type;
@@ -94,6 +97,7 @@ struct option {
        int flags;
        parse_opt_cb *callback;
        intptr_t defval;
+       bool *set;
 };
 
 #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
@@ -103,6 +107,10 @@ struct option {
 #define OPT_GROUP(h)                { .type = OPTION_GROUP, .help = (h) }
 #define OPT_BIT(s, l, v, h, b)      { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
 #define OPT_BOOLEAN(s, l, v, h)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
+#define OPT_BOOLEAN_SET(s, l, v, os, h) \
+       { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \
+       .value = check_vtype(v, bool *), .help = (h), \
+       .set = check_vtype(os, bool *)}
 #define OPT_INCR(s, l, v, h)        { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
 #define OPT_SET_UINT(s, l, v, h, i)  { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
 #define OPT_SET_PTR(s, l, v, h, p)  { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
index c232d8dd410bf6483ba8fcd850380bbd562f03ee..56fc10a5e288142a04bda6d4086c14763531a11f 100644 (file)
@@ -1,19 +1,23 @@
 #include <linux/list.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <dirent.h>
 #include "fs.h"
+#include <locale.h>
 #include "util.h"
 #include "pmu.h"
 #include "parse-events.h"
 #include "cpumap.h"
 
+#define UNIT_MAX_LEN   31 /* max length for event unit name */
+
 struct perf_pmu_alias {
        char *name;
        struct list_head terms;
        struct list_head list;
+       char unit[UNIT_MAX_LEN+1];
+       double scale;
 };
 
 struct perf_pmu_format {
@@ -94,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format)
        return 0;
 }
 
-static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
+static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name)
+{
+       struct stat st;
+       ssize_t sret;
+       char scale[128];
+       int fd, ret = -1;
+       char path[PATH_MAX];
+       char *lc;
+
+       snprintf(path, PATH_MAX, "%s/%s.scale", dir, name);
+
+       fd = open(path, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       if (fstat(fd, &st) < 0)
+               goto error;
+
+       sret = read(fd, scale, sizeof(scale)-1);
+       if (sret < 0)
+               goto error;
+
+       scale[sret] = '\0';
+       /*
+        * save current locale
+        */
+       lc = setlocale(LC_NUMERIC, NULL);
+
+       /*
+        * force to C locale to ensure kernel
+        * scale string is converted correctly.
+        * kernel uses default C locale.
+        */
+       setlocale(LC_NUMERIC, "C");
+
+       alias->scale = strtod(scale, NULL);
+
+       /* restore locale */
+       setlocale(LC_NUMERIC, lc);
+
+       ret = 0;
+error:
+       close(fd);
+       return ret;
+}
+
+static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name)
+{
+       char path[PATH_MAX];
+       ssize_t sret;
+       int fd;
+
+       snprintf(path, PATH_MAX, "%s/%s.unit", dir, name);
+
+       fd = open(path, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+               sret = read(fd, alias->unit, UNIT_MAX_LEN);
+       if (sret < 0)
+               goto error;
+
+       close(fd);
+
+       alias->unit[sret] = '\0';
+
+       return 0;
+error:
+       close(fd);
+       alias->unit[0] = '\0';
+       return -1;
+}
+
+static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
 {
        struct perf_pmu_alias *alias;
        char buf[256];
@@ -110,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
                return -ENOMEM;
 
        INIT_LIST_HEAD(&alias->terms);
+       alias->scale = 1.0;
+       alias->unit[0] = '\0';
+
        ret = parse_events_terms(&alias->terms, buf);
        if (ret) {
                free(alias);
@@ -117,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
        }
 
        alias->name = strdup(name);
+       /*
+        * load unit name and scale if available
+        */
+       perf_pmu__parse_unit(alias, dir, name);
+       perf_pmu__parse_scale(alias, dir, name);
+
        list_add_tail(&alias->list, list);
+
        return 0;
 }
 
@@ -129,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
 {
        struct dirent *evt_ent;
        DIR *event_dir;
+       size_t len;
        int ret = 0;
 
        event_dir = opendir(dir);
@@ -143,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
                if (!strcmp(name, ".") || !strcmp(name, ".."))
                        continue;
 
+               /*
+                * skip .unit and .scale info files
+                * parsed in perf_pmu__new_alias()
+                */
+               len = strlen(name);
+               if (len > 5 && !strcmp(name + len - 5, ".unit"))
+                       continue;
+               if (len > 6 && !strcmp(name + len - 6, ".scale"))
+                       continue;
+
                snprintf(path, PATH_MAX, "%s/%s", dir, name);
 
                ret = -EINVAL;
                file = fopen(path, "r");
                if (!file)
                        break;
-               ret = perf_pmu__new_alias(head, name, file);
+
+               ret = perf_pmu__new_alias(head, dir, name, file);
                fclose(file);
        }
 
@@ -508,16 +607,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
        return NULL;
 }
 
+
+static int check_unit_scale(struct perf_pmu_alias *alias,
+                           char **unit, double *scale)
+{
+       /*
+        * Only one term in event definition can
+        * define unit and scale, fail if there's
+        * more than one.
+        */
+       if ((*unit && alias->unit) ||
+           (*scale && alias->scale))
+               return -EINVAL;
+
+       if (alias->unit)
+               *unit = alias->unit;
+
+       if (alias->scale)
+               *scale = alias->scale;
+
+       return 0;
+}
+
 /*
  * Find alias in the terms list and replace it with the terms
  * defined for the alias
  */
-int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
+int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
+                         char **unit, double *scale)
 {
        struct parse_events_term *term, *h;
        struct perf_pmu_alias *alias;
        int ret;
 
+       *unit   = NULL;
+       *scale  = 0;
+
        list_for_each_entry_safe(term, h, head_terms, list) {
                alias = pmu_find_alias(pmu, term);
                if (!alias)
@@ -525,6 +650,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
                ret = pmu_alias_terms(alias, &term->list);
                if (ret)
                        return ret;
+
+               ret = check_unit_scale(alias, unit, scale);
+               if (ret)
+                       return ret;
+
                list_del(&term->list);
                free(term);
        }
index 1179b26f244a31280e5454787a51a8f01ff9ac2e..9183380e203851b8fb12f0bcee499b61dcda0f58 100644 (file)
@@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
 int perf_pmu__config_terms(struct list_head *formats,
                           struct perf_event_attr *attr,
                           struct list_head *head_terms);
-int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
+int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
+                         char **unit, double *scale);
 struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
                                  struct list_head *head_terms);
 int perf_pmu_wrap(void);
index f36d24a024453f6e2777d1bc4cfbb10fb9a3cf4a..b0b15e213df50d9624674b7f9de622a48b90b800 100644 (file)
@@ -1522,6 +1522,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
                        if (!node)
                                break;
 
+                       if (node->sym && node->sym->ignore)
+                               goto next;
+
                        if (print_ip)
                                printf("%c%16" PRIx64, s, node->ip);
 
@@ -1544,12 +1547,15 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
                        if (!print_oneline)
                                printf("\n");
 
-                       callchain_cursor_advance(&callchain_cursor);
-
                        stack_depth--;
+next:
+                       callchain_cursor_advance(&callchain_cursor);
                }
 
        } else {
+               if (al.sym && al.sym->ignore)
+                       return;
+
                if (print_ip)
                        printf("%16" PRIx64, sample->ip);
 
index 96c866045d60d2a17f4d969ed5e3a0a3c00933fc..8b79d3ad124629e44184b8d20474592f9346a44b 100644 (file)
@@ -95,6 +95,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)
 
        total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;
        fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n");
+       fprintf(svgfile, "<!DOCTYPE svg SYSTEM \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
        fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height);
 
        fprintf(svgfile, "<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n");
@@ -128,12 +129,33 @@ void svg_box(int Yslot, u64 start, u64 end, const char *type)
                time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
 }
 
-void svg_sample(int Yslot, int cpu, u64 start, u64 end)
+static char *time_to_string(u64 duration);
+void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
+{
+       if (!svgfile)
+               return;
+
+       fprintf(svgfile, "<g>\n");
+       fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu,
+               time_to_string(end - start));
+       if (backtrace)
+               fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace);
+       svg_box(Yslot, start, end, "blocked");
+       fprintf(svgfile, "</g>\n");
+}
+
+void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
 {
        double text_size;
        if (!svgfile)
                return;
 
+       fprintf(svgfile, "<g>\n");
+
+       fprintf(svgfile, "<title>#%d running %s</title>\n",
+               cpu, time_to_string(end - start));
+       if (backtrace)
+               fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace);
        fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n",
                time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT);
 
@@ -148,6 +170,7 @@ void svg_sample(int Yslot, int cpu, u64 start, u64 end)
                fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n",
                        time2pixels(start), Yslot *  SLOT_MULT + SLOT_HEIGHT - 1, text_size,  cpu + 1);
 
+       fprintf(svgfile, "</g>\n");
 }
 
 static char *time_to_string(u64 duration)
@@ -168,7 +191,7 @@ static char *time_to_string(u64 duration)
        return text;
 }
 
-void svg_waiting(int Yslot, u64 start, u64 end)
+void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace)
 {
        char *text;
        const char *style;
@@ -192,6 +215,9 @@ void svg_waiting(int Yslot, u64 start, u64 end)
        font_size = round_text_size(font_size);
 
        fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT);
+       fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start));
+       if (backtrace)
+               fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace);
        fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
                time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style);
        if (font_size > MIN_TEXT_SIZE)
@@ -242,6 +268,8 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
        max_freq = __max_freq;
        turbo_frequency = __turbo_freq;
 
+       fprintf(svgfile, "<g>\n");
+
        fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",
                time2pixels(first_time),
                time2pixels(last_time)-time2pixels(first_time),
@@ -253,6 +281,8 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
 
        fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",
                10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model());
+
+       fprintf(svgfile, "</g>\n");
 }
 
 void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name)
@@ -264,6 +294,7 @@ void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name
 
 
        fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu));
+       fprintf(svgfile, "<title>%s %s</title>\n", name, time_to_string(end - start));
        fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
                time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type);
        width = time2pixels(end)-time2pixels(start);
@@ -288,6 +319,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)
                return;
 
 
+       fprintf(svgfile, "<g>\n");
+
        if (type > 6)
                type = 6;
        sprintf(style, "c%i", type);
@@ -306,6 +339,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type)
        if (width > MIN_TEXT_SIZE)
                fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n",
                        time2pixels(start), cpu2y(cpu)+width, width, type);
+
+       fprintf(svgfile, "</g>\n");
 }
 
 static char *HzToHuman(unsigned long hz)
@@ -339,6 +374,8 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
        if (!svgfile)
                return;
 
+       fprintf(svgfile, "<g>\n");
+
        if (max_freq)
                height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);
        height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height;
@@ -347,10 +384,11 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
        fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n",
                time2pixels(start), height+0.9, HzToHuman(freq));
 
+       fprintf(svgfile, "</g>\n");
 }
 
 
-void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2)
+void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace)
 {
        double height;
 
@@ -358,6 +396,15 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
                return;
 
 
+       fprintf(svgfile, "<g>\n");
+
+       fprintf(svgfile, "<title>%s wakes up %s</title>\n",
+               desc1 ? desc1 : "?",
+               desc2 ? desc2 : "?");
+
+       if (backtrace)
+               fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
+
        if (row1 < row2) {
                if (row1) {
                        fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
@@ -395,9 +442,11 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
        if (row1)
                fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(32,255,32)\"/>\n",
                        time2pixels(start), height);
+
+       fprintf(svgfile, "</g>\n");
 }
 
-void svg_wakeline(u64 start, int row1, int row2)
+void svg_wakeline(u64 start, int row1, int row2, const char *backtrace)
 {
        double height;
 
@@ -405,6 +454,11 @@ void svg_wakeline(u64 start, int row1, int row2)
                return;
 
 
+       fprintf(svgfile, "<g>\n");
+
+       if (backtrace)
+               fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
+
        if (row1 < row2)
                fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
                        time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT,  time2pixels(start), row2 * SLOT_MULT);
@@ -417,17 +471,28 @@ void svg_wakeline(u64 start, int row1, int row2)
                height += SLOT_HEIGHT;
        fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(32,255,32)\"/>\n",
                        time2pixels(start), height);
+
+       fprintf(svgfile, "</g>\n");
 }
 
-void svg_interrupt(u64 start, int row)
+void svg_interrupt(u64 start, int row, const char *backtrace)
 {
        if (!svgfile)
                return;
 
+       fprintf(svgfile, "<g>\n");
+
+       fprintf(svgfile, "<title>Wakeup from interrupt</title>\n");
+
+       if (backtrace)
+               fprintf(svgfile, "<desc>%s</desc>\n", backtrace);
+
        fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(255,128,128)\"/>\n",
                        time2pixels(start), row * SLOT_MULT);
        fprintf(svgfile, "<circle  cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\"  style=\"fill:rgb(255,128,128)\"/>\n",
                        time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT);
+
+       fprintf(svgfile, "</g>\n");
 }
 
 void svg_text(int Yslot, u64 start, const char *text)
@@ -455,6 +520,7 @@ void svg_legenda(void)
        if (!svgfile)
                return;
 
+       fprintf(svgfile, "<g>\n");
        svg_legenda_box(0,      "Running", "sample");
        svg_legenda_box(100,    "Idle","c1");
        svg_legenda_box(200,    "Deeper Idle", "c3");
@@ -462,6 +528,7 @@ void svg_legenda(void)
        svg_legenda_box(550,    "Sleeping", "process2");
        svg_legenda_box(650,    "Waiting for cpu", "waiting");
        svg_legenda_box(800,    "Blocked on IO", "blocked");
+       fprintf(svgfile, "</g>\n");
 }
 
 void svg_time_grid(void)
index e0781989cc31f5d1dd004dc66c22a584d371189d..fad79ceeac1adb12470d0a7f842e4787f52f2dad 100644 (file)
@@ -5,8 +5,9 @@
 
 extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
 extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
-extern void svg_sample(int Yslot, int cpu, u64 start, u64 end);
-extern void svg_waiting(int Yslot, u64 start, u64 end);
+extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
+extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
+extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace);
 extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
 
 
@@ -17,9 +18,9 @@ extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);
 
 extern void svg_time_grid(void);
 extern void svg_legenda(void);
-extern void svg_wakeline(u64 start, int row1, int row2);
-extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2);
-extern void svg_interrupt(u64 start, int row);
+extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace);
+extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace);
+extern void svg_interrupt(u64 start, int row, const char *backtrace);
 extern void svg_text(int Yslot, u64 start, const char *text);
 extern void svg_close(void);
 
index c0c36965fff0f0060b2f2a077edc17c569648bf2..360eefecb81df4de3d458085ba6cbc05dce0f0d4 100644 (file)
@@ -573,6 +573,36 @@ static u8 kallsyms2elf_type(char type)
        return isupper(type) ? STB_GLOBAL : STB_LOCAL;
 }
 
+bool symbol__is_idle(struct symbol *sym)
+{
+       const char * const idle_symbols[] = {
+               "cpu_idle",
+               "intel_idle",
+               "default_idle",
+               "native_safe_halt",
+               "enter_idle",
+               "exit_idle",
+               "mwait_idle",
+               "mwait_idle_with_hints",
+               "poll_idle",
+               "ppc64_runlatch_off",
+               "pseries_dedicated_idle_sleep",
+               NULL
+       };
+
+       int i;
+
+       if (!sym)
+               return false;
+
+       for (i = 0; idle_symbols[i]; i++) {
+               if (!strcmp(idle_symbols[i], sym->name))
+                       return true;
+       }
+
+       return false;
+}
+
 static int map__process_kallsym_symbol(void *arg, const char *name,
                                       char type, u64 start)
 {
@@ -1496,14 +1526,15 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
 
        build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
 
+       scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir,
+                 sbuild_id);
+
        /* Use /proc/kallsyms if possible */
        if (is_host) {
                DIR *d;
                int fd;
 
                /* If no cached kcore go with /proc/kallsyms */
-               scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s",
-                         buildid_dir, sbuild_id);
                d = opendir(path);
                if (!d)
                        goto proc_kallsyms;
@@ -1528,6 +1559,10 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
                goto proc_kallsyms;
        }
 
+       /* Find kallsyms in build-id cache with kcore */
+       if (!find_matching_kcore(map, path, sizeof(path)))
+               return strdup(path);
+
        scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s",
                  buildid_dir, sbuild_id);
 
@@ -1719,7 +1754,7 @@ out_fail:
        return -1;
 }
 
-static int setup_list(struct strlist **list, const char *list_str,
+int setup_list(struct strlist **list, const char *list_str,
                      const char *list_name)
 {
        if (list_str == NULL)
index 07de8fea2f48dbc346124539e7393307b839051a..f1031a1358a1ee3bbb12b62c87dfc83497d7ff92 100644 (file)
@@ -240,6 +240,7 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp);
 bool symbol_type__is_a(char symbol_type, enum map_type map_type);
 bool symbol__restricted_filename(const char *filename,
                                 const char *restricted_filename);
+bool symbol__is_idle(struct symbol *sym);
 
 int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
                  struct symsrc *runtime_ss, symbol_filter_t filter,
@@ -273,4 +274,7 @@ void kcore_extract__delete(struct kcore_extract *kce);
 int kcore_copy(const char *from_dir, const char *to_dir);
 int compare_proc_modules(const char *from, const char *to);
 
+int setup_list(struct strlist **list, const char *list_str,
+              const char *list_name);
+
 #endif /* __PERF_SYMBOL */
index 3c778a07b7cc78de8d92ec188e9bb446c40798bd..e74c5963dc7a9db7038e4ad3f5f7de1ed490f20f 100644 (file)
@@ -55,6 +55,13 @@ enum target_errno target__validate(struct target *target)
                        ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM;
        }
 
+       /* THREAD and SYSTEM/CPU are mutually exclusive */
+       if (target->per_thread && (target->system_wide || target->cpu_list)) {
+               target->per_thread = false;
+               if (ret == TARGET_ERRNO__SUCCESS)
+                       ret = TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD;
+       }
+
        return ret;
 }
 
@@ -100,6 +107,7 @@ static const char *target__error_str[] = {
        "UID switch overriding CPU",
        "PID/TID switch overriding SYSTEM",
        "UID switch overriding SYSTEM",
+       "SYSTEM/CPU switch overriding PER-THREAD",
        "Invalid User: %s",
        "Problems obtaining information for user %s",
 };
@@ -131,7 +139,8 @@ int target__strerror(struct target *target, int errnum,
        msg = target__error_str[idx];
 
        switch (errnum) {
-       case TARGET_ERRNO__PID_OVERRIDE_CPU ... TARGET_ERRNO__UID_OVERRIDE_SYSTEM:
+       case TARGET_ERRNO__PID_OVERRIDE_CPU ...
+            TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD:
                snprintf(buf, buflen, "%s", msg);
                break;
 
index 89bab7129de4a3c92b74ca037787431c155f251f..31dd2e9a27d043fd0609bfed57ea6a0f9076c6d9 100644 (file)
@@ -12,6 +12,8 @@ struct target {
        uid_t        uid;
        bool         system_wide;
        bool         uses_mmap;
+       bool         default_per_cpu;
+       bool         per_thread;
 };
 
 enum target_errno {
@@ -32,6 +34,7 @@ enum target_errno {
        TARGET_ERRNO__UID_OVERRIDE_CPU,
        TARGET_ERRNO__PID_OVERRIDE_SYSTEM,
        TARGET_ERRNO__UID_OVERRIDE_SYSTEM,
+       TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD,
 
        /* for target__parse_uid() */
        TARGET_ERRNO__INVALID_UID,
index cd8e2f59271969f410daf28afdf93523699f475c..49eaf1d7d89d0554a146a272e923cd139e6cbac7 100644 (file)
@@ -70,14 +70,13 @@ int thread__set_comm(struct thread *thread, const char *str, u64 timestamp)
        /* Override latest entry if it had no specific time coverage */
        if (!curr->start) {
                comm__override(curr, str, timestamp);
-               return 0;
+       } else {
+               new = comm__new(str, timestamp);
+               if (!new)
+                       return -ENOMEM;
+               list_add(&new->list, &thread->comm_list);
        }
 
-       new = comm__new(str, timestamp);
-       if (!new)
-               return -ENOMEM;
-
-       list_add(&new->list, &thread->comm_list);
        thread->comm_set = true;
 
        return 0;
index 897c1b2a750a278c8cecf000c892d5f6bba8f0af..5b856bf942e11fa9691c28551a88b53b4ac3d5f2 100644 (file)
@@ -6,6 +6,7 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include "symbol.h"
+#include <strlist.h>
 
 struct thread {
        union {
@@ -66,4 +67,15 @@ static inline void thread__set_priv(struct thread *thread, void *p)
 {
        thread->priv = p;
 }
+
+static inline bool thread__is_filtered(struct thread *thread)
+{
+       if (symbol_conf.comm_list &&
+           !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) {
+               return true;
+       }
+
+       return false;
+}
+
 #endif /* __PERF_THREAD_H */
index 5390d0b8862a680e147cf52dd8bed22cfbba333f..0efd5393de85ea33775198f25045cfa00a612247 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 ec0c71a2ca2e22f2c78ef90726d0e9afe623b43c..d5966f49e22cc24fb91e8f53307504cfed7e290d 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 4178effd9e99ce217c2c65a34dcfe09b4eb5edfa..7b3646adb92f597cf9016c88e3c5d97e12f2b9c0 100644 (file)
@@ -87,4 +87,5 @@ Thomas Renninger <trenn@suse.de>
 .fi
 .SH "SEE ALSO"
 .LP
-cpupower(1), cpupower\-monitor(1), cpupower\-info(1), cpupower\-set(1)
+cpupower(1), cpupower\-monitor(1), cpupower\-info(1), cpupower\-set(1),
+cpupower\-idle\-set(1)
diff --git a/tools/power/cpupower/man/cpupower-idle-set.1 b/tools/power/cpupower/man/cpupower-idle-set.1
new file mode 100644 (file)
index 0000000..6b16072
--- /dev/null
@@ -0,0 +1,71 @@
+.TH "CPUPOWER-IDLE-SET" "1" "0.1" "" "cpupower Manual"
+.SH "NAME"
+.LP
+cpupower idle\-set \- Utility to set cpu idle state specific kernel options
+.SH "SYNTAX"
+.LP
+cpupower [ \-c cpulist ] idle\-info [\fIoptions\fP]
+.SH "DESCRIPTION"
+.LP
+The cpupower idle\-set subcommand allows to set cpu idle, also called cpu
+sleep state, specific options offered by the kernel. One example is disabling
+sleep states. This can be handy for power vs performance tuning.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-d\fR \fB\-\-disable\fR
+Disable a specific processor sleep state.
+.TP
+\fB\-e\fR \fB\-\-enable\fR
+Enable a specific processor sleep state.
+
+.SH "REMARKS"
+.LP
+Cpuidle Governors Policy on Disabling Sleep States
+
+.RS 4
+Depending on the used  cpuidle governor, implementing the kernel policy
+how to choose sleep states, subsequent sleep states on this core, might get
+disabled as well.
+
+There are two cpuidle governors ladder and menu. While the ladder
+governor is always available, if CONFIG_CPU_IDLE is selected, the
+menu governor additionally requires CONFIG_NO_HZ.
+
+The behavior and the effect of the disable variable depends on the
+implementation of a particular governor. In the ladder governor, for
+example, it is not coherent, i.e. if one is disabling a light state,
+then all deeper states are disabled as well. Likewise, if one enables a
+deep state but a lighter state still is disabled, then this has no effect.
+.RE
+.LP
+Disabling the Lightest Sleep State may not have any Affect
+
+.RS 4
+If criteria are not met to enter deeper sleep states and the lightest sleep
+state is chosen when idle, the kernel may still enter this sleep state,
+irrespective of whether it is disabled or not. This is also reflected in
+the usage count of the disabled sleep state when using the cpupower idle-info
+command.
+.RE
+.LP
+Selecting specific CPU Cores
+
+.RS 4
+By default processor sleep states of all CPU cores are set. Please refer
+to the cpupower(1) manpage in the \-\-cpu option section how to disable
+C-states of specific cores.
+.RE
+.SH "FILES"
+.nf
+\fI/sys/devices/system/cpu/cpu*/cpuidle/state*\fP
+\fI/sys/devices/system/cpu/cpuidle/*\fP
+.fi
+.SH "AUTHORS"
+.nf
+Thomas Renninger <trenn@suse.de>
+.fi
+.SH "SEE ALSO"
+.LP
+cpupower(1), cpupower\-monitor(1), cpupower\-info(1), cpupower\-set(1),
+cpupower\-idle\-info(1)
index 5cdc600e8152efae140687df6a532933e196cf59..851c7a16ca49633f0db3fd155b15466dfc4ab4fa 100644 (file)
@@ -278,7 +278,7 @@ static char *sysfs_idlestate_get_one_string(unsigned int cpu,
 int sysfs_is_idlestate_disabled(unsigned int cpu,
                                unsigned int idlestate)
 {
-       if (sysfs_get_idlestate_count(cpu) < idlestate)
+       if (sysfs_get_idlestate_count(cpu) <= idlestate)
                return -1;
 
        if (!sysfs_idlestate_file_exists(cpu, idlestate,
@@ -303,7 +303,7 @@ int sysfs_idlestate_disable(unsigned int cpu,
        char value[SYSFS_PATH_MAX];
        int bytes_written;
 
-       if (sysfs_get_idlestate_count(cpu) < idlestate)
+       if (sysfs_get_idlestate_count(cpu) <= idlestate)
                return -1;
 
        if (!sysfs_idlestate_file_exists(cpu, idlestate,
index fe702076ca46cc2d3d02bab818446c9d15f8c392..9d77f13c2d2548934293662e314828a817d16144 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 da7a19558281637789bd932614861cc10c15d692..bdb71a26ae35d7d058f87fa94e6bf2e39e558015 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 d053ea40c001ff8ecfba849560d819c9886c212a..14a4f4cab5b97a1070abc656bca1bc72c60b5aa1 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 779262f59e252b458a67335635b59d0d1ca1932e..fbe1a48bd6292b575d349eced2d0628d9082b1db 100644 (file)
@@ -27,3 +27,6 @@ config HAVE_KVM_MSI
 
 config HAVE_KVM_CPU_RELAX_INTERCEPT
        bool
+
+config KVM_VFIO
+       bool
index 8a39dda7a3254677df5996c07c965a2f9f5ef8a8..8631d9c14320bea69b4e9713013ab54b2e3752be 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 72a130bc448aa97adb1ca35a2dfff09a102b717d..0df7d4b34dfec96345d359bf08562309d8607f40 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 1cf9ccb010131d42c3a573079437d87502ac016f..a0aa84b5941ac96aabae48b03d80278052ce8929 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
 /*
@@ -2270,6 +2275,11 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
        case KVM_DEV_TYPE_XICS:
                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,
+};